我有一个显示数据网格的表单。我还有一个在不同线程上运行的方法,它只更新网格的显示单元格。为此,此方法调用窗体上的函数,该函数返回显示的单元格。
我遇到的问题是,有时在窗体关闭并处理时,另一个线程上的方法仍在调用此函数,这会导致对象处置异常。有没有办法(除了确保另一个线程上的方法已经完成)以防止这种情况?
所以我需要一个线程安全的方法来在窗体关闭时终止后台任务。
private delegate List<foo> GetShownCellsDelegate();
public List<foo> GetShownCells()
{
if (this.InvokeRequired)
{
GetShownCellsDelegate getShownCellsDelegate = new GetShownCellsDelegate(GetShownCells);
return (List<foo>)this.Invoke(getShownCellsDelegate);
}
else
{
//do stuff
}
}
我尝试使用表单的IsDisposed属性:
if (!IsDisposed)
{
return (List<foo>)this.Invoke(getShownCellsDelegate);
}
但显然表单可以在if语句之后处理,因为我仍然得到了isdisposed异常。
这就是我在另一个线程上使用该函数的方法:
private CancellationTokenSource cts = new CancellationTokenSource();
public void CancelUpdate()
{
cts.Cancel();
}
public void ReadDataFromDevice()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ReadAllDataThreadPoolMethod));
}
private void ReadAllDataThreadPoolMethod(Object stateInfo)
{
if (!cts.IsCancellationRequested)
{
//do stuff
}
}
从表单上的IsClosing事件调用CancelUpdate方法。但有时我仍然会得到被处置的异常。
答案 0 :(得分:5)
要取消长时间运行操作,您可以使用专为合作取消而设计的CancellationToken
。
让主窗体在启动后台线程时创建CancellationTokenSource
,将CTS生成的CacellationToken
传递给后台线程,在窗体关闭时取消CTS,然后使用后台线程在尝试调用主线程之前检查令牌是否被取消。
public void Foo()
{
var cts = new CancellationTokenSource();
var task = Task.Run(() => DoWork(cts.Token));
FormClosing += (s, args) =>
{
cts.Cancel();
if (!task.IsCompleted)
{
args.Cancel = true;
task.ContinueWith(t => Close());
}
};
}
private void DoWork(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Do some work
}
}
为了确保后台线程没有通过取消检查,然后屈服于UI线程让它取消令牌并处理表单,在工作完成之前,你还需要确保在窗体关闭之前,后台线程在取消后有时间运行完成。这可以通过结束处理程序中的简单Thread.Join
调用来完成。
答案 1 :(得分:0)
this.FormClosed += new FormClosedEventHandler(form1_FormClosed);
void form1_FormClosed(object sender, FormClosedEventArgs e)
{
//close thread
}
只要您的表单被关闭,就会执行此操作。