在使用CancellationTokenSource取消任务后,我在等待任务时遇到了问题。取消调用不会中断任务。当我等待任务时,主线程会阻塞,因为任务永远不会中断。
下面是我的程序的简短描述:一个任务递增一个char变量(从'A‘到'Z'),并在GUI线程上显示它。为此,该任务在创建控件的线程上执行一个委托(this.invoke())。
只要我注释掉RefreshTextBox()-Function,cancel调用就会起作用,任务就会中断。看起来好像this.invoke()命令阻止了任务中断。
在下面的代码中,我也用普通线程实现了相同的功能。然后我就开始工作。任务实现和线程实现的区别在哪里?
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
public partial class frm_Main : Form
{
private delegate void dgt_StringHandler(string str_Value);
CancellationTokenSource _obj_Cts = null;
Thread _obj_Thread = null;
Task _obj_Task = null;
public frm_Main()
{
InitializeComponent();
}
private void CreateChar(ref char chr_Value)
{
int int_Value;
int_Value = (int)chr_Value;
int_Value++;
if (int_Value > 90 || int_Value < 65)
int_Value = 65;
chr_Value = (char)int_Value;
}
private void TestThread()
{
char chr_Value = '@';
bool bol_Stop = false;
while (!bol_Stop)
{
try
{
Thread.Sleep(300);
CreateChar(ref chr_Value);
RefreshTextBox(chr_Value.ToString());
}
catch (ThreadInterruptedException)
{
bol_Stop = true;
}
}
}
private void TestTask(object obj_TokenTmp)
{
char chr_Value = '@';
CancellationToken obj_Token = (CancellationToken)obj_TokenTmp;
while (!obj_Token.IsCancellationRequested)
{
Thread.Sleep(300);
CreateChar(ref chr_Value);
RefreshTextBox(chr_Value.ToString());
}
}
private void RefreshTextBox(string str_Value)
{
if (txt_Value.InvokeRequired)
{
dgt_StringHandler obj_StringHandler = new dgt_StringHandler(RefreshTextBox);
this.Invoke(obj_StringHandler, new object[] { str_Value });
}
else
{
txt_Value.Text = str_Value;
}
}
private void btn_StartStop_Click(object sender, EventArgs e)
{
if (_obj_Task == null && _obj_Thread == null)
{
if (opt_Task.Checked)
{
_obj_Cts = new CancellationTokenSource();
_obj_Task = new Task(new Action<object>(TestTask), _obj_Cts.Token, _obj_Cts.Token);
_obj_Task.Start();
}
else
{
_obj_Thread = new Thread(new ThreadStart(TestThread));
_obj_Thread.Start();
}
btn_StartStop.Text = "Stop";
}
else
{
if (_obj_Thread != null)
{
_obj_Thread.Interrupt();
_obj_Thread.Join();
_obj_Thread = null;
}
if (_obj_Task != null)
{
_obj_Cts.Cancel();
_obj_Task.Wait();
_obj_Task = null;
_obj_Cts = null;
}
btn_StartStop.Text = "Start";
}
}
}发布于 2011-12-02 21:29:55
这两段代码一起形成了一个死锁:
_obj_Cts.Cancel();
_obj_Task.Wait();和
this.Invoke(obj_StringHandler, new object[] { str_Value });您在主线程上调用Wait(),而Invoke()需要由主线程处理。
您可以通过使用this.BeginInvoke(...)来打破死锁。
线程版本使用中断,一个大锤。这样线程就不会在停止信号之后尝试调用RefreshTextBox()。
发布于 2011-12-05 18:41:12
以下是修改后的代码。我现在调用BeginInvoke()而不是Henk Holterman提议的Invoke()。这非常有效,并且是防止死锁的唯一正确方法。还有另一种情况必须考虑。我还有一个通过BeginInvoke()调用给出的IAsyncResult对象。这个对象我用来检查异步调用是否已经完成。如果我不检查它,可能是GUI线程执行委托的速度不够快(例如,GUI线程中的某个休眠语句),因此我的TestTask()方法总是调用BeginInvoke(),尽管GUI线程还没有完成最后一个委托。结果是我的GUI线程将阻塞应用程序。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
namespace InvokeTest
{
public partial class frm_Main : Form
{
private delegate void dgt_StringHandler(string str_Value);
CancellationTokenSource _obj_Cts = null;
Thread _obj_Thread = null;
Task _obj_Task = null;
IAsyncResult _obj_Ar = null;
public frm_Main()
{
InitializeComponent();
}
private void CreateChar(ref char chr_Value)
{
int int_Value;
int_Value = (int)chr_Value;
int_Value++;
if (int_Value > 90 || int_Value < 65)
int_Value = 65;
chr_Value = (char)int_Value;
}
private void TestThread()
{
char chr_Value = '@';
bool bol_Stop = false;
while (!bol_Stop)
{
try
{
Thread.Sleep(1); // is needed for interrupting the thread
CreateChar(ref chr_Value);
RefreshTextBox(chr_Value.ToString());
}
catch (ThreadInterruptedException)
{
bol_Stop = true;
}
}
}
private void TestTask(object obj_TokenTmp)
{
char chr_Value = '@';
CancellationToken obj_Token = (CancellationToken)obj_TokenTmp;
while (!obj_Token.IsCancellationRequested)
{
CreateChar(ref chr_Value);
RefreshTextBox(chr_Value.ToString());
}
}
private void RefreshTextBox(string str_Value)
{
if (txt_Value.InvokeRequired)
{
if (_obj_Ar == null ||
_obj_Ar.IsCompleted)
{
dgt_StringHandler obj_StringHandler = new dgt_StringHandler(RefreshTextBox);
_obj_Ar = this.BeginInvoke(obj_StringHandler, new object[] { str_Value });
}
}
else
{
Thread.Sleep(200);
txt_Value.Text = str_Value;
}
}
private void btn_StartStop_Click(object sender, EventArgs e)
{
if (_obj_Task == null && _obj_Thread == null)
{
if (opt_Task.Checked)
{
_obj_Cts = new CancellationTokenSource();
_obj_Task = new Task(new Action<object>(TestTask), _obj_Cts.Token, _obj_Cts.Token);
_obj_Task.Start();
}
else
{
_obj_Thread = new Thread(new ThreadStart(TestThread));
_obj_Thread.Start();
}
btn_StartStop.Text = "Stop";
}
else
{
if (_obj_Thread != null)
{
_obj_Thread.Interrupt();
_obj_Thread.Join();
_obj_Thread = null;
}
if (_obj_Task != null)
{
_obj_Cts.Cancel();
_obj_Task.Wait();
_obj_Task = null;
_obj_Cts = null;
}
btn_StartStop.Text = "Start";
}
}
private void frm_Main_FormClosing(object sender, FormClosingEventArgs e)
{
if (_obj_Thread != null)
{
_obj_Thread.Interrupt();
_obj_Thread.Join();
}
if (_obj_Task != null)
{
_obj_Cts.Cancel();
_obj_Task.Wait();
}
}
}
}https://stackoverflow.com/questions/8356533
复制相似问题