亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

循環不會因 Thread 和 CancellationToken 而停止

循環不會因 Thread 和 CancellationToken 而停止

C#
拉風的咖菲貓 2023-08-13 13:55:41
我正在使用異步回調的 Windows 套接字應用程序。如果我使用 Thread 來啟動_StartListening,當我調用時StopListening,循環仍然停止在allDone.WaitOne().?但Task版本就可以了。有什么不同?我的代碼是這個的修改版本原始版本有felix-bManualResetEvent提到的競爭條件。我把它改成了,但問題仍然存在。SemaphoreSlim我在調試模式下嘗試過,即使我不啟動客戶端,if (cancelToken.IsCancellationRequested)在我調用后似乎也不會遇到斷點。StopListening對不起。我發現我不小心啟動了兩個socket服務器。那就是問題所在。?class WinSocketServer:IDisposable? {? ? ? ? public SemaphoreSlim semaphore = new SemaphoreSlim(0);? ? ? ? private CancellationTokenSource cancelSource = new CancellationTokenSource();? ? ? ? public void AcceptCallback(IAsyncResult ar)? ? ? ? {? ? ? ? ? ? semaphore.Release();? ? ? ? ? ? //Do something? ? ? ? }? ? ? ? private void _StartListening(CancellationToken cancelToken)? ? ? ? {? ? ? ? ? ? try? ? ? ? ? ? {? ? ? ? ? ? ? ? while (true)? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? if (cancelToken.IsCancellationRequested)? ? ? ? ? ? ? ? ? ? ? ? break;? ? ? ? ? ? ? ? ? ? Console.WriteLine("Waiting for a connection...");? ? ? ? ? ? ? ? ? ? listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);? ? ? ? ? ? ? ? ? ? semaphore.Wait();? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? ? ? catch (Exception e)? ? ? ? ? ? {? ? ? ? ? ? ? ? Console.WriteLine(e.ToString());? ? ? ? ? ? }? ? ? ? ? ? Console.WriteLine("Complete");? ? ? ? }? ? ? ? public void StartListening()? ? ? ? {? ? ? ? ? ? Task.Run(() => _StartListening(cancelSource.Token));//OK? ? ? ? ? ? var t = new Thread(() => _StartListening(cancelSource.Token));? ? ? ? ? ? t.Start();//Can't be stopped by calling StopListening? ? ? ? }? ? ? ? public void StopListening()? ? ? ? {? ? ? ? ? ? listener.Close();? ? ? ? ? ? cancelSource.Cancel();? ? ? ? ? ? semaphore.Release();? ? ? ? }? ? ? ? public void Dispose()? ? ? ? {? ? ? ? ? ? StopListening();? ? ? ? ? ? cancelSource.Dispose();? ? ? ? ? ? semaphore.Dispose();? ? ? ? }? ? }
查看完整描述

1 回答

?
慕哥9229398

TA貢獻1877條經驗 獲得超6個贊

您的代碼存在競爭條件,可能會導致死鎖(有時)。讓我們將線程命名為“監聽器”(運行的線程_StartListening)和“控制”(運行的線程StopListening):

  1. 偵聽器線程:if (cancelToken.IsCancellationRequested)-> false

  2. 控制線程:cancelSource.Cancel()

  3. 控制線程:allDone.Set()

  4. 偵聽器線程:allDone.Reset()-> 意外重置停止請求!

  5. 監聽線程:listener.BeginAccept(...)

  6. 控制線程:stopListening()退出,而監聽器繼續工作!

  7. 偵聽器線程:allDone.WaitOne()-> 死鎖!沒有人會這么做allDone.Set()。

問題在于如何使用該allDone事件,應該是相反的:應該在它因任何原因退出之前_StartListening執行,而應該執行:allDone.Set()StopListeningallDone.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退出。


線程/任務行為的差異確實很奇怪,它可能源于任務線程是后臺線程,而常規線程是前臺線程。


查看完整回答
反對 回復 2023-08-13
  • 1 回答
  • 0 關注
  • 111 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號