1 回答

TA貢獻(xiàn)1877條經(jīng)驗(yàn) 獲得超6個贊
您的代碼存在競爭條件,可能會導(dǎo)致死鎖(有時(shí))。讓我們將線程命名為“監(jiān)聽器”(運(yùn)行的線程_StartListening
)和“控制”(運(yùn)行的線程StopListening
):
偵聽器線程:
if (cancelToken.IsCancellationRequested)
-> false控制線程:
cancelSource.Cancel()
控制線程:
allDone.Set()
偵聽器線程:
allDone.Reset()
-> 意外重置停止請求!監(jiān)聽線程:
listener.BeginAccept(...)
控制線程:
stopListening()
退出,而監(jiān)聽器繼續(xù)工作!偵聽器線程:
allDone.WaitOne()
-> 死鎖!沒有人會這么做allDone.Set()
。
問題在于如何使用該allDone
事件,應(yīng)該是相反的:應(yīng)該在它因任何原因退出之前_StartListening
執(zhí)行,而應(yīng)該執(zhí)行: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直到有新的客戶端連接才會返回。停止監(jiān)聽時(shí),需要關(guān)閉socket( listener.Close())才能強(qiáng)制BeginAccept退出。
線程/任務(wù)行為的差異確實(shí)很奇怪,它可能源于任務(wù)線程是后臺線程,而常規(guī)線程是前臺線程。
- 1 回答
- 0 關(guān)注
- 108 瀏覽
添加回答
舉報(bào)