我们最近采用了TPL作为运行一些繁重后台任务的工具包。
这些任务通常会生成一个实现IDisposable
的对象。这是因为它内部有一些操作系统句柄。
我想要发生的是后台线程生成的对象将始终正确处理,也就是当切换与应用程序关闭一致时。
经过一番思考,我写了这个:
private void RunOnUiThread(Object data, Action<Object> action)
{
var t = Task.Factory.StartNew(action, data, CancellationToken.None, TaskCreationOptions.None, _uiThreadScheduler);
t.ContinueWith(delegate(Task task)
{
if (!task.IsCompleted)
{
DisposableObject.DisposeObject(task.AsyncState);
}
});
}
后台Task
调用RunOnUiThread
将其结果传递给UI线程。任务t
在UI线程上安排,并获得传入的data
的所有权。我原以为如果由于ui线程的消息泵关闭而无法执行t
,继续运行,我可以看到任务失败了,并自己处理对象。 DisposeObject()
是一个帮助程序,在处理它之前检查对象是否实际上是IDisposable,并且是非null。
可悲的是,它不起作用。如果在创建后台任务t
后关闭应用程序,则不会执行继续。
我之前解决了这个问题。那时我使用Threadpool和WPF Dispatcher在UI线程上发布消息。它不是很漂亮,但最终它起作用了。我希望TPL在这种情况下更好。如果我能以某种方式告诉TPL它应该处理所有剩余的AsyncState对象(如果它们实现IDisposable),那会更好。
所以,代码主要是为了说明问题。我想了解任何允许我从后台任务安全地将Disposable对象切换到UI线程的解决方案,最好是尽可能少的代码。
答案 0 :(得分:1)
当进程关闭时,它的所有内核句柄都会自动关闭。你不应该担心这个:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686722(v=vs.85).aspx
答案 1 :(得分:0)
看看RX库。这可以让你做你想做的事。
答案 2 :(得分:0)
来自MSDN:
当任务处于三者之一时,
IsCompleted
将返回true 最终状态:RanToCompletion
,Faulted
或Canceled
换句话说,永远不会调用DisposableObject.DisposeObject
,因为在上述某个条件发生后,将始终安排继续。我相信你的意思是:
t.ContinueWith(t => DisposableObject.DisposeObject(task.AsyncState),
TaskContinuationOptions.NotOnRanToCompletion)
(顺便说一句,你可以简单地捕获data
变量,而不是使用AsyncState
属性)
但是,我不会对你想要确保在任何时候发生的事情使用延续。我相信try-finally
块更适合这里:
private void RunOnUiThread2(Object data, Action<Object> action)
{
var t = Task.Factory.StartNew(() =>
{
try
{
action(data);
}
finally
{
DisposableObject.DisposeObject(task.AsyncState);
//Or use a new *foreground* thread if the disposing is heavy
}
}, CancellationToken.None, TaskCreationOptions.None, _uiThreadScheduler);
}