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 关注
- 89 浏览
添加回答
举报