听起来像生产者 - 消费者设计可能很适合你的问题。一般而言,客户端线程会将接收到的数据放入(线程安全)队列中,之后不会对其进行修改 - 任何到达的新数据都会进入队列中的新插槽。然后,主线程可以等待任何队列中的新数据并在其到达时进行处理。主线程可以定期检查所有队列,或者(更好)在数据放入队列时接收某种通知,以便在没有任何事情发生时可以休眠,并且不会占用CPU时间。
既然你问锁:这是一个基本的基于锁的实现作为替代队列,也许,这将有助于你明白一个道理
class IncomingClientData
{
private List<byte> m_incomingData = new List<byte>();
private readonly object m_lock = new object();
public void Append(IEnumerable<byte> data)
{
lock(m_lock)
{
m_incomingData.AddRange(data);
}
}
public List<byte> ReadAndClear()
{
lock(m_lock)
{
List<byte> result = m_incomingData;
m_incomingData = new List<byte>();
return result;
}
}
}
在这个例子中,客户端线程将调用Append
与他们收到的数据以及主线程可以通过调用ReadAndClear
收集自上次检查以来收到的所有收到的数据。
这是线程安全的通过锁定在这两个函数的所有代码上m_lock
,这只是一个普通的简单的对象 - 你可以在C#中的任何物体锁定,但我相信这可能会造成混淆,实际上导致微妙如果不小心使用了错误,所以我几乎总是使用专用对象来锁定。一次只有一个线程可以锁定对象,因此这些函数的代码一次只能在一个线程中运行。例如,如果您的主线程在客户线程仍然忙于将数据追加到列表中时调用ReadAndClear
,那么主线程将等待客户线程离开Append
函数。
这不是要求为此创建一个新类,但它可以防止意外,因为我们可以小心控制共享状态如何被访问。例如,我们知道在ReadAndClear()
中返回内部列表是安全的,因为当时不能有其他参考。
现在为您的第二个问题:只是简单地调用一个方法将永远不会导致该方法在不同的线程上运行,无论该方法在哪个类中。Invoke
是WinForms UI线程的一个特殊功能,您如果你想在你的工作线程中使用Invoke
,你必须自己实现这个功能。在内部,Invoke
的工作方式是将要运行的代码放入应该在UI线程上运行的所有事物的队列中,包括UI事件。 UI线程本身基本上是一个循环,它总是从该队列中提取下一项工作,然后执行该工作,然后从队列中获取下一项,等等。这也是为什么你不应该在事件处理程序中长时间工作 - 只要UI线程忙于运行你的代码,它将无法处理其队列中的下一个项目,所以你会支持所有发生的其他工作项目/事件。
如果你想要你的客户端线程运行某个功能,你必须实际提供代码 - 例如让客户端线程检查某个队列中主线程的命令。
恐怕这个描述太含糊了。代码会更好。我不明白你会如何得到更多的一般性建议。 – usr
其实它只是一般的建议,我需要:) –
你必须使用锁定来防止事故。如果你有一个不重要的客户端连接数,这将对吞吐量产生非常不利的影响,所有这些线程都会在锁上争夺。你解决这个问题是不要这样做,最好的一般建议。 –