C# 这个代码[理论上]线程不安全吗?
我在编写的代码中遇到了一个奇怪的死锁 其思想是实现一个异步操作,其Stop是同步的——调用者必须等待它完成。我已经将实际工作的部分简化为一个简单的属性增量(C# 这个代码[理论上]线程不安全吗?,c#,multithreading,deadlock,manualresetevent,C#,Multithreading,Deadlock,Manualresetevent,我在编写的代码中遇到了一个奇怪的死锁 其思想是实现一个异步操作,其Stop是同步的——调用者必须等待它完成。我已经将实际工作的部分简化为一个简单的属性增量(++Value,见下文);但实际上,调用了一个对线程非常敏感的重COM组件 我遇到的死锁出现在Stop()方法中,在该方法中,我显式地等待识别已完成操作的手动重置事件 你知道我会做错什么吗?代码应该是自包含的,并且可以自己编译 using System; using System.ComponentModel; using System.Th
++Value
,见下文);但实际上,调用了一个对线程非常敏感的重COM组件
我遇到的死锁出现在Stop()
方法中,在该方法中,我显式地等待识别已完成操作的手动重置事件
你知道我会做错什么吗?代码应该是自包含的,并且可以自己编译
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
using ThreadingTimer = System.Threading.Timer;
namespace CS_ManualResetEvent
{
class AsyncOperation
{
ThreadingTimer myTimer; //!< Receives periodic ticks on a ThreadPool thread and dispatches background worker.
ManualResetEvent myBgWorkerShouldIterate; //!< Fired when background worker must run a subsequent iteration of its processing loop.
ManualResetEvent myBgWorkerCompleted; //!< Fired before the background worker routine exits.
BackgroundWorker myBg; //!< Executes a background tasks
int myIsRunning; //!< Nonzero if operation is active; otherwise, zero.
public AsyncOperation()
{
var aTimerCallbac = new TimerCallback(Handler_Timer_Tick);
myTimer = new ThreadingTimer(aTimerCallbac, null, Timeout.Infinite, 100);
myBg = new BackgroundWorker();
myBg.DoWork += new DoWorkEventHandler(Handler_BgWorker_DoWork);
myBgWorkerShouldIterate = new ManualResetEvent(false);
myBgWorkerCompleted = new ManualResetEvent(false);
}
public int Value { get; set; }
/// <summary>Begins an asynchronous operation.</summary>
public void Start()
{
Interlocked.Exchange(ref myIsRunning, 1);
myTimer.Change(0, 100);
myBg.RunWorkerAsync(null);
}
/// <summary>Stops the worker thread and waits until it finishes.</summary>
public void Stop()
{
Interlocked.Exchange(ref myIsRunning, 0);
myTimer.Change(-1, Timeout.Infinite);
// fire the event once more so that the background worker can finish
myBgWorkerShouldIterate.Set();
// Wait until the operation completes; DEADLOCK occurs HERE!!!
myBgWorkerCompleted.WaitOne();
// Restore the state of events so that we could possibly re-run an existing component.
myBgWorkerCompleted.Reset();
myBgWorkerShouldIterate.Reset();
}
void Handler_BgWorker_DoWork(object sender, EventArgs theArgs)
{
while (true)
{
myBgWorkerShouldIterate.WaitOne();
if (myIsRunning == 0)
{
//Thread.Sleep(5000); //What if it takes some noticeable time to finish?
myBgWorkerCompleted.Set();
break;
}
// pretend we're doing some valuable work
++Value;
// The event will be set back in Handler_Timer_Tick or when the background worker should finish
myBgWorkerShouldIterate.Reset();
}
// exit
}
/// <summary>Processes tick events from a timer on a dedicated (thread pool) thread.</summary>
void Handler_Timer_Tick(object state)
{
// Let the asynchronous operation run its course.
myBgWorkerShouldIterate.Set();
}
}
public partial class Form1 : Form
{
private AsyncOperation myRec;
private Button btnStart;
private Button btnStop;
public Form1()
{
InitializeComponent();
}
private void Handler_StartButton_Click(object sender, EventArgs e)
{
myRec = new AsyncOperation();
myRec.Start();
btnStart.Enabled = false;
btnStop.Enabled = true;
}
private void Handler_StopButton_Click(object sender, EventArgs e)
{
myRec.Stop();
// Display the result of the asynchronous operation.
MessageBox.Show (myRec.Value.ToString() );
btnStart.Enabled = true;
btnStop.Enabled = false;
}
private void InitializeComponent()
{
btnStart = new Button();
btnStop = new Button();
SuspendLayout();
// btnStart
btnStart.Location = new System.Drawing.Point(35, 16);
btnStart.Size = new System.Drawing.Size(97, 63);
btnStart.Text = "Start";
btnStart.Click += new System.EventHandler(Handler_StartButton_Click);
// btnStop
btnStop.Enabled = false;
btnStop.Location = new System.Drawing.Point(138, 16);
btnStop.Size = new System.Drawing.Size(103, 63);
btnStop.Text = "Stop";
btnStop.Click += new System.EventHandler(Handler_StopButton_Click);
// Form1
ClientSize = new System.Drawing.Size(284, 94);
Controls.Add(this.btnStop);
Controls.Add(this.btnStart);
Text = "Form1";
ResumeLayout(false);
}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
使用系统;
使用系统组件模型;
使用系统线程;
使用System.Windows.Forms;
使用ThreadingTimer=System.Threading.Timer;
命名空间CS_ManualResetEvent
{
类异步操作
{
ThreadingTimer myTimer;//!<接收线程池线程上的周期性标记并分派后台工作线程。
当后台工作程序必须运行其处理循环的后续迭代时,ManualResetEvent MyBGWorkerShouldItemRate;//!<将触发。
ManualResetEvent myBgWorkerCompleted;//!<在后台工作程序例程退出之前激发。
BackgroundWorker myBg;//!<执行后台任务
int myIsRunning;//!<如果操作处于活动状态,则为非零;否则为零。
公共异步操作()
{
var aTimerCallbac=新的TimerCallback(处理器计时器刻度);
myTimer=newThreadingTimer(aTimerCallbac,null,Timeout.Infinite,100);
myBg=新的BackgroundWorker();
myBg.DoWork+=新的DoWorkEventHandler(Handler\u BgWorker\u DoWork);
MyBGWorkerSholdeTerate=新手动重置事件(假);
myBgWorkerCompleted=新手动重置事件(false);
}
公共int值{get;set;}
///开始异步操作。
公开作废开始()
{
联锁交换(参考myIsRunning,1);
myTimer.Change(01100);
myBg.RunWorkerAsync(空);
}
///停止工作线程并等待它完成。
公共停车场()
{
联锁交换(参考myIsRunning,0);
myTimer.Change(-1,Timeout.Infinite);
//再次触发事件,以便后台工作人员可以完成
MyBGWorkerShouldItemerate.Set();
//等待操作完成;此处发生死锁!!!
myBgWorkerCompleted.WaitOne();
//恢复事件的状态,以便我们可以重新运行现有组件。
myBgWorkerCompleted.Reset();
mybgWorkerShouldItemerate.Reset();
}
无效处理程序\u BgWorker\u DoWork(对象发送方、事件args和args)
{
while(true)
{
mybgWorkerShouldItemate.WaitOne();
如果(myIsRunning==0)
{
//Thread.Sleep(5000);//如果需要一些明显的时间才能完成呢?
myBgWorkerCompleted.Set();
打破
}
//假装我们在做一些有价值的工作
++价值观;
//事件将在处理程序\u计时器\u勾选中或后台工作程序应完成时设置回原位
mybgWorkerShouldItemerate.Reset();
}
//出口
}
///在专用(线程池)线程上处理来自计时器的滴答事件。
无效处理程序\计时器\勾号(对象状态)
{
//让异步操作按其路线运行。
MyBGWorkerShouldItemerate.Set();
}
}
公共部分类Form1:Form
{
私有异步操作myRec;
私人按钮btnStart;
私人按钮btnStop;
公共表格1()
{
初始化组件();
}
私有无效处理程序\u开始按钮\u单击(对象发送方,事件参数e)
{
myRec=新的异步操作();
myRec.Start();
btnStart.Enabled=false;
btnStop.Enabled=true;
}
私有无效处理程序\u停止按钮\u单击(对象发送者,事件参数e)
{
myRec.Stop();
//显示异步操作的结果。
Show(myRec.Value.ToString());
btnStart.Enabled=true;
btnStop.Enabled=false;
}
私有void InitializeComponent()
{
btnStart=新建按钮();
btnStop=新按钮();
SuspendLayout();
//btnStart
btnStart.Location=新系统图纸点(35,16);
btnStart.Size=新系统图纸尺寸(97,63);
btnStart.Text=“开始”;
btnStart.Click+=newsystem.EventHandler(Handler\u StartButton\u Click);
//btnStop
btnStop.Enabled=false;
btnStop.Location=新系统图纸点(138,16);
btnStop.Size=新系统图纸尺寸(103,63);
btnStop.Text=“停止”;
btnStop.Click+=newsystem.EventHandler(Handler\u StopButton\u Click);
//表格1
ClientSize=新系统图纸尺寸(284,94);
控件。添加(this.btnStop);
控件。添加(this.btnStart);
Text=“Form1”;
简历布局(假);
}
}
静态类程序
{
///
///应用程序的主要入口点。
///
[状态线程]
静态void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(新Form1());
}
}
}
将此代码置于++值下;在处理工工作中。然后公关
int i = 0;
while (i++ < 100) {
System.Diagnostics.Debug.Print("Press the button now");
Thread.Sleep(300);
Application.DoEvents();
}
private CancellationTokenSource cancellationSource;
private Task asyncOperationCompleted;
private void button1_Click(object sender, EventArgs e)
{
//cancel previously running operation before starting a new one
if (cancellationSource != null)
{
cancellationSource.Cancel();
}
else //take out else if you want to restart here when `start` is pressed twice.
{
cancellationSource = new CancellationTokenSource();
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
asyncOperationCompleted = tcs.Task;
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += (_, args) => DoWork(bgw, cancellationSource);
bgw.ProgressChanged += (_, args) => label1.Text = args.ProgressPercentage.ToString();
bgw.WorkerReportsProgress = true;
bgw.RunWorkerCompleted += (_, args) => tcs.SetResult(true);
bgw.RunWorkerAsync();
}
}
private void DoWork(BackgroundWorker bgw, CancellationTokenSource cancellationSource)
{
int i = 0;
while (!cancellationSource.IsCancellationRequested)
{
Thread.Sleep(1000);//placeholder for real work
bgw.ReportProgress(i++);
}
}
private void StopAndWaitOnBackgroundTask()
{
if (cancellationSource != null)
{
cancellationSource.Cancel();
cancellationSource = null;
asyncOperationCompleted.Wait();
}
}