以下是我的代码描述的问题:
public partial class Form1 : Form
{
private readonly object lockObject = new object();
public Form1()
{
InitializeComponent();
}
private void btnWorkerThread_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => SomeLongRunningThread());
}
private void SomeLongRunningThread()
{
lock (lockObject)
{
Thread.Sleep(3000);
if (txtResult.InvokeRequired)
{
Thread.Sleep(3000);
txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); });
}
else
{
Thread.Sleep(3000);
txtResult.Text = DateTime.Now.ToShortTimeString();
}
}
}
private void btnUIThread_Click(object sender, EventArgs e)
{
SomeLongRunningThread();
}
}
当工作线程启动然后btnUIThread发生了点击死锁:
似乎在txtResult.InvokeRequried:
上发生了死锁txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); });
如果我错了,请纠正我。
问题是如何处理死锁?
编辑:经过研究员的有价值的答案后,我的实际项目比预期的要复杂得多,并且锁定块有更多代码,我应该保证一次只能由一个线程执行。
答案 0 :(得分:3)
为了避免死锁,您应lock
采用不同的方式。锁定所有线程代码没有意义。
此外,此锁(lockobject)不是必需的,因为在您的示例SomeAction()
已经同步以在主/ UI线程中执行。
private void SomeLongRunningThread()
{
Thread.Sleep(3000);
if (txtResult.InvokeRequired)
{
Thread.Sleep(3000);
txtResult.Invoke((MethodInvoker) delegate { SomeAction });
}
else
{
Thread.Sleep(3000);
SomeAction();
}
}
private void SomeAction(){
// This lock is not needed as long this method is only called from SomeLongRunningThread()
//lock (lockObject)
//{
txtResult.Text = DateTime.Now.ToShortTimeString();
//}
}
答案 1 :(得分:3)
此处无需lock
。
调用txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); });
将委托推送到UI消息循环队列。当循环准备就绪时,它会弹出委托并执行。用户界面只能同时运行一件事 - 没有比赛,也没有死锁。
答案 2 :(得分:2)
.NET 4.5引入了IProgress< T>接口来报告线程和任务的进度,以及运行回调的Progress< T>实现,或者在创建它的线程中引发事件。有了这个,async/await
,您就不需要调用BeginInvoke
或`Invoke。
使用Progress
:
public partial class Form1 : Form
{
IProgress<string> _progress;
public Form1()
{
InitializeComponent();
_progress = new Progress<string>(UpdateUI);
}
void UpdateUI(string message)
{
txtResult.Text = message;
}
private void btnWorkerThread_Click(object sender, EventArgs e)
{
Task.Run(() => SomeLongRunningThread());
}
private void SomeLongRunningThread()
{
Thread.Sleep(3000);
_progress.Report(DateTime.Now.ToShortTimeString());
}
private void btnUIThread_Click(object sender, EventArgs e)
{
SomeLongRunningThread();
}
}