trigger event from an AsyncCallback in c#
My question involves events and where I am triggering the events in my class. This class wraps my TCP functionality and I am using TcpListener to achieve this. I realize some TCP stuff may be missing from the following example but I want to make things as simple as possible:
c# 2.0 sample
class MyTcpClass
{
public delegate void ClientConnectHandler(Socket client, int clientNum);
public event ClientConnectHandler ClientConnect;
private Socket wellKnownSocket;
private Socket[] clientSockets = new Socket[MAX_CLIENTS];
private int numConnected = 0;
private void OnClientConnect(Socket client, int clientNum)
{
if (ClientConnect != null)
ClientConnect(client, clientNum);
}
public void StartListening()
{
//initialize wellKnownSocket
//...
wellKnownSocket.BeginAccept(new AsyncCallback(internal_clientConnect);
}
public void internal_clientConnect(IAsyncResult ar)
{
//Add client socket to clientSocket[numConnected]
//numConnected++;
//...
wellKnownSocket.EndAccept(ar);
OnClientConnect(clientSocket[numConnected], numConnected);
//error: event happens on different thread!!
}
}
class MainForm
{
void Button_click()
{
MyTcpClass mtc = new MyTcpClass();
mtc.ClientConnect += mtc_ClientConnected;
}
void mtc_clientConnected(Socket c开发者_运维百科lient, int clientNum)
{
ActivityListBox.Items.Add("Client #" + clientNum.ToString() + " connected.");
//exception: cannot modify control on seperate thread
}
}
I guess my question is, without breaking this pattern too much, what makes more sense? Also, if anyone has a better more elegant solution they are welcome.
Theory
class MainForm
{
public MainForm()
{
MyTcpClass mtc = new MyTcpClass();
MyTcpClass2 mtc2 = new MyTcpClass2(this);
//this version holds a Form handle to invoke the event
mtc.ClientConnect += mtc_uglyClientConnect;
mtc2.ClientConnect += mtc2_smartClientConnect;
}
//This event is being called in the AsyncCallback of MyTcpClass
//the main form handles invoking the control, I want to avoid this
void mtc_uglyClientConnect(Socket s, int n)
{
if (mycontrol.InvokeRequired)
{
//call begininvoke to update mycontrol
}
else
{
mycontrol.text = "Client " + n.ToString() + " connected.";
}
}
//This is slightly cleaner, as it is triggered in MyTcpClass2 by using its
//passed in Form handle's BeginInvoke to trigger the event on its own thread.
//However, I also want to avoid this because referencing a form in a seperate class
//while having it (the main form) observe events in the class as well seems... bad
void mtc2_smartClientConnect(Socket s, int n)
{
mycontrol.text = "Client " + n.ToString() + " connected.";
}
}
While you did not post the code for MyTcpClass2
, I'm pretty sure I see what you're getting at.
No, I wouldn't do it the second way. Because, for example, if anything else ever needs to bind to that event at the same time, you are going to force it to run on another thread.
In short, the method that receives the event should be responsible for running whatever code it needs to on whatever thread it needs to. The mechanism that raises the event should be completely oblivious to any sort of weird threading stuff that the receiver requires. Aside from complicating the multiple-event-binding scenario, it moves the logic of cross-thread invocation into a class where it doesn't belong. The MyTcpClass
class should be focused on handling TCP client/server matters, not mucking around with Winforms threads.
精彩评论