使用 TPL 将“Disposable”对象安全地传递到 UI 线程
我们最近采用 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
的所有权。我期望如果t
无法执行因为 ui 线程的消息泵已关闭,所以延续将运行,我可以看到任务失败,并自行处置该对象。 DisposeObject()
是一个帮助器,用于在处置对象之前检查该对象是否实际上是 IDisposable 且非 null。
可悲的是,它不起作用。如果我在创建后台任务 t 后关闭应用程序,则不会执行延续。
我之前解决过这个问题。当时我正在使用 Threadpool 和 WPF Dispatcher 在 UI 线程上发布消息。虽然不是很漂亮,但最终还是成功了。我希望 TPL 在这种情况下能做得更好。如果我能以某种方式教导 TPL,如果它们实现了 IDisposable,它应该处理所有剩余的 AsyncState 对象,那就更好了。
所以,代码主要是为了说明问题。我想了解任何允许我将 Disposable 对象从后台任务安全地移交给 UI 线程的解决方案,并且最好是代码尽可能少的解决方案。
We recently adopted the TPL as the toolkit for running some heavy background tasks.
These tasks typically produce a single object that implements IDisposable
. This is because it has some OS handles internally.
What I want to happen is that the object produced by the background thread will be properly disposed at all times, also when the handover coincides with application shutdown.
After some thinking, I wrote this:
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);
}
});
}
The background Task
calls RunOnUiThread
to pass its result to the UI thread. The task t
is scheduled on the UI thread, and takes ownership of the data
passed in. I was expecting that if t
could not be executed because the ui thread's message pump was shut down, the continuation would run, and I could see that that the task had failed, and dispose the object myself. DisposeObject()
is a helper that checks if the object is actually IDisposable, and non-null, prior to disposing it.
Sadly, it does not work. If I close the application after the background task t
is created, the continuation is not executed.
I solved this problem before. At that time I was using the Threadpool and the WPF Dispatcher to post messages on the UI thread. It wasn't very pretty, but in the end it worked. I was hoping that the TPL was better at this scenario. It would even be better if I could somehow teach the TPL that it should Dispose all leftover AsyncState objects if they implement IDisposable.
So, the code is mainly to illustrate the problem. I want to learn about any solution that allows me to safely handover Disposable objects to the UI thread from background Tasks, and preferably one with as little code as possible.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当进程关闭时,它的所有内核句柄都会自动关闭。您不必担心这一点:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686722(v=vs.85).aspx
When a process closes, all of it's kernel handles are automatically closed. You shouldn't need to worry about this:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686722(v=vs.85).aspx
查看 RX 库。这可能会让你做你想做的事。
Have a look at the RX library. This may allow you to do what you want.
来自 MSDN:
换句话说,您的
DisposableObject.DisposeObject
永远不会被调用,因为在上述条件之一发生之后,总是会安排继续。我相信你的意思是:(顺便说一句,你可以简单地捕获
data
变量,而不是使用AsyncState
属性)但是我不会对某些事情使用延续您希望确保始终发生这种情况。我相信
try-finally
块在这里更合适:From MSDN:
In other words, your
DisposableObject.DisposeObject
will never be called, because the continuation will always be scheduled after one of the above conditions has taken place. I believe what you meant to do was :(BTW you could have simply captured the
data
variable rather than using theAsyncState
property)However I wouldn't use a continuation for something that you want to ensure happens at all times. I believe a
try-finally
block will be more fitting here: