聽起來像生產者 - 消費者設計可能很適合你的問題。一般而言,客戶端線程會將接收到的數據放入(線程安全)隊列中,之後不會對其進行修改 - 任何到達的新數據都會進入隊列中的新插槽。然後,主線程可以等待任何隊列中的新數據並在其到達時進行處理。主線程可以定期檢查所有隊列,或者(更好)在數據放入隊列時接收某種通知,以便在沒有任何事情發生時可以休眠,並且不會佔用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
其實它只是一般的建議,我需要:) –
你必須使用鎖定來防止事故。如果你有一個不重要的客戶端連接數,這將對吞吐量產生非常不利的影響,所有這些線程都會在鎖上爭奪。你解決這個問題是不要這樣做,最好的一般建議。 –