1 回答

TA貢獻1877條經驗 獲得超6個贊
您的代碼存在競爭條件,可能會導致死鎖(有時)。讓我們將線程命名為“監聽器”(運行的線程_StartListening
)和“控制”(運行的線程StopListening
):
偵聽器線程:
if (cancelToken.IsCancellationRequested)
-> false控制線程:
cancelSource.Cancel()
控制線程:
allDone.Set()
偵聽器線程:
allDone.Reset()
-> 意外重置停止請求!監聽線程:
listener.BeginAccept(...)
控制線程:
stopListening()
退出,而監聽器繼續工作!偵聽器線程:
allDone.WaitOne()
-> 死鎖!沒有人會這么做allDone.Set()
。
問題在于如何使用該allDone
事件,應該是相反的:應該在它因任何原因退出之前_StartListening
執行,而應該執行:allDone.Set()
StopListening
allDone.WaitOne()
class WinSocketServer:IDisposable
{
// I guess this was in your code, necessary to show proper stopping
private Socket listener = new Socket(......);
public ManualResetEvent allDone = new ManualResetEvent(false);
private CancellationTokenSource cancelSource = new CancellationTokenSource();
private void _StartListening(CancellationToken cancelToken)
{
try
{
listener.Listen(...); // I guess
allDone.Reset(); // reset once before starting the loop
while (!cancelToken.IsCancellationRequested)
{
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
allDone.Set(); // notify that the listener is exiting
Console.WriteLine("Complete");
}
public void StartListening()
{
Task.Run(() => _StartListening(cancelSource.Token));
}
public void StopListening()
{
// notify the listener it should exit
cancelSource.Cancel();
// cancel possibly pending BeginAccept
listener.Close();
// wait until the listener notifies that it's actually exiting
allDone.WaitOne();
}
public void Dispose()
{
StopListening();
cancelSource.Dispose();
allDone.Dispose();
}
}
更新
值得注意的是,listener.BeginAccept直到有新的客戶端連接才會返回。停止監聽時,需要關閉socket( listener.Close())才能強制BeginAccept退出。
線程/任務行為的差異確實很奇怪,它可能源于任務線程是后臺線程,而常規線程是前臺線程。
- 1 回答
- 0 關注
- 111 瀏覽
添加回答
舉報