c# thread. join()阻塞主线程
本文关键字:线程 thread join | 更新日期: 2023-09-27 18:17:31
我有3个线程在运行:主线程,readData线程和获取线程。在表单中,当单击play按钮时,它启动设备获取和readData线程。当停止按钮被按下时,我想停止两个线程。然而,acquitionthread . 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
设置为什么,它都不会终止线程,直到该方法返回。
您应该查看任务并行库和可取消的任务,原始线程对于更高级别的控制正在贬值。也可以考虑async/await,因为你正在阻塞I/O,当你可以等待一个I/O绑定任务时,真的没有理由让一个新线程坐下来等待I/O。