![](/img/trans.png)
[英]c# Update label from sub-thread when using thread.join on the main thread
[英]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.