簡體   English   中英

C#Thread.Join()阻塞主線程

[英]C# Thread.Join() blocks the main thread

我有3個線程在運行:主線程,readData線程和獲取線程。 在窗體中,單擊播放按鈕后,它將啟動設備獲取和readData線程。 當按下“停止”按鈕時,我想停止兩個線程。 但是,acqusitionThread.Join()阻止執行。 我究竟做錯了什么?

主要形式

 private void btnPlay_Click(object sender, EventArgs e)
        {
          daqObj.Start();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
          daqObj.Stop();
        }

數據獲取類,用於從設備讀取數據

     public void Start()
            {
                _isRunning = true;

            acquisitionDevice.StartAcquisition(); //starts thread for acquisition

                //start data acquisition thread
                _readDataThread = new Thread(readData);
                _readThread.Name = "Read data Thread";
                _redThread.Priority = ThreadPriority.AboveNormal;
                _readThread.Start();
            }

       public void ReadData()
        {

            try
            {
                // write data to file
                while (_isRunning)
                {
                    //Reads data (dequeues from buffer)
                    float[] data = acquisitionDevice.ReadData(numValuesAtOnce);

                    //Do other stuff with data (eg: save to file)
                }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("\t{0}", ex.Message);
                }
    }


            public void Stop()
            {
                _isRunning = false;

                    if ((_writeToFileThread != null) && _writeToFileThread.IsAlive)
                        _writeToFileThread.Join(); //stop readData thread

                    acquisitionDevice.StopAcquisition(); //stops acquisition thread

                     Console.WriteLine("acquisiton thread stopped); //THIS IS NEVER EXECUTED

            }

設備獲取類別:

 public void StartAcquisition(Dictionary<string, DeviceConfiguration> deviceSerials)
        {
            //ensure that data acquisition is not already running
            if (_isRunning || (_acquisitionThread != null && _acquisitionThread.IsAlive))
                throw new InvalidOperationException("Data acquisition is already running!");

            _isRunning = true;

            //initialize buffer
            _buffer = new WindowedBuffer<float>(BufferSizeSeconds * sampleRate * totalChannels);

            //start data acquisition thread
            _acquisitionThread = new Thread(DoAcquisition);
            _acquisitionThread.Name = "DataAcquisition Thread";
            _acquisitionThread.Priority = ThreadPriority.Highest;
            _acquisitionThread.Start(deviceSerials);
        }

 public void StopAcquisition()
        {
            //tell the data acquisition thread to stop
            _isRunning = false;

            //wait until the thread has stopped data acquisition
            if (_acquisitionThread != null)
                _acquisitionThread.Join(); //THIS BLOCKS

            Console.WriteLine("ended"); //THIS IS NEVER EXECUTED
        }

編輯我在令牌取消內執行此操作,而不是使用單獨的線程讀取數據。 我使用一個單獨的線程從設備中獲取數據(這是連續獲取數據所必需的),然后讀取它並將其寫入具有令牌取消功能的文件中,這是有效的代碼:

public void StartAcquisition()
            {
                // Initialize token
                _cancellationTokenSourceObj = new CancellationTokenSource();
                var token = _cancellationTokenSourceObj.Token;

                    Task.Factory.StartNew(() =>
               {

                   // Start acquisition
                   try
                   {
                       // Write device configuration parameters to .txt file
                       System.IO.StreamWriter file = new System.IO.StreamWriter(deviceConfFilePath);
                       file.WriteLine(gUSBampObj.GetDeviceConfigurationString());
                       file.Close();

                       // create file stream
                       using (_fileStream = new FileStream(daqFilePath, FileMode.Create))
                       {
                           using (BinaryWriter writer = new BinaryWriter(_fileStream))
                           {
                               // start acquisition thread
                               deviceAcquisition.StartAcquisition();

                               // write data to file
                               while (!token.IsCancellationRequested)
                               {
                                   float[] data = deviceAcquisition.ReadData(numValuesAtOnce);

                                   // write data to file
                                   for (int i = 0; i < data.Length; i++)
                                       writer.Write(data[i]);
                               }
                           }
                       }
                   }
                   catch (Exception ex)
                   {
                       Console.WriteLine("\t{0}", ex.Message);
                   }

               }, token)
           .ContinueWith(t =>
           {
               //This will run after stopping, close files and devices here

               // stop data acquisition
               deviceAcquisition.StopAcquisition();

           });


                }
            }

public void StopAcquisition()
{
    _cancellationTokenSourceObj.Cancel();
}

您不想阻塞並等待另一個線程完成。 那就是Thread.Join()所做的。

相反,您想執行線程取消。 MSDN托管線程取消

根據設計, Thread.Join()是來自MSDN的阻塞調用:

Join是一種同步方法,它將阻塞調用線程 (即,調用該方法的線程),直到完成其Join方法被調用的線程為止。 使用此方法可確保線程已終止。 如果線程沒有終止,則調用方將無限期阻塞。

(強調我的)

這是設計使然,因為您不會使用超時調用其中一個重載。 但是,您的代碼還有另一個問題,您可能不會像預期那樣向線程發出終止信號。

多數民眾贊成在volatile關鍵字出現的地方,您應該使用它聲明isRunning字段,如下所示:

private volatile bool _isRunning;

這將確保編譯器不對字段值使用單線程緩存優化,並在每次讀取該字段時獲取最新值。 由於要從多個線程更新該字段,因此需要將其標記為volatile

您遇到的另一個問題是while循環:

while (_isRunning)
{
    //Reads data (dequeues from buffer)
    float[] data = acquisitionDevice.ReadData(numValuesAtOnce);

    //Do other stuff with data (eg: save to file)
}

問題是這一行:

float[] data = acquisitionDevice.ReadData(numValuesAtOnce);

如果那是一個阻塞調用,並且ReadData不返回,則無論您將_isRunning設置_isRunning ,它都不會終止線程,直到該方法返回為止。

您應該查看“ 任務並行庫”和可取消的任務,原始線程為了更高級別的控制而被棄用。 還應考慮使用async / await ,因為您正在阻塞I / O,所以實際上只要您等待I / O綁定的任務,就沒有理由等待新線程坐下來等待I / O。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM