我正在处理一个托管对象在async方法中间过早完成的情况.
这是一个业余爱好家庭自动化项目(Windows 8.1,.NET 4.5.1),我向非托管第三方DLL提供C#回调.在某个传感器事件时调用回调.
为了处理这个事件,我使用async/await了一个简单的自定义awaiter(而不是TaskCompletionSource).我这样做的部分原因是为了减少不必要的分配数量,但主要是出于好奇心作为学习练习.
下面是我所拥有的非常剥离的版本,使用Win32计时器队列计时器来模拟非托管事件源.让我们从输出开始:
Press Enter to exit... Awaiter() tick: 0 tick: 1 ~Awaiter() tick: 2 tick: 3 tick: 4
请注意我的等待者在第二次打勾后如何最终确定.这是出乎意料的.
代码(控制台应用程序):
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static async Task TestAsync()
{
var awaiter = new Awaiter();
//var hold = GCHandle.Alloc(awaiter);
WaitOrTimerCallbackProc callback = (a, b) =>
awaiter.Continue();
IntPtr timerHandle;
if (!CreateTimerQueueTimer(out timerHandle,
IntPtr.Zero,
callback,
IntPtr.Zero, 500, 500, 0))
throw new System.ComponentModel.Win32Exception( …Run Code Online (Sandbox Code Playgroud) .net c# garbage-collection task-parallel-library async-await
说我有以下课程:
class SomeClass
{
private TaskCompletionSource<string> _someTask;
public Task<string> WaitForThing()
{
_someTask = new TaskCompletionSource<string>();
return _someTask.Task;
}
//Other code which calls _someTask.SetResult(..);
}
Run Code Online (Sandbox Code Playgroud)
然后别的,我打电话
//Some code..
await someClassInstance.WaitForThing();
//Some more code
Run Code Online (Sandbox Code Playgroud)
在//Some more code被调用之前不会被_someTask.SetResult(..)调用.调用上下文在内存中等待.
但是,假设SetResult(..)从未被调用过,并且someClassInstance停止被引用并被垃圾收集.这会造成内存泄漏吗?或.Net自动神奇地知道需要处理调用上下文?
当使用API和从任务并行库派生的类时,开发人员何时需要关注垃圾收集的影响?
.NET Task实例在运行期间是否超出范围?,似乎给人一种安全感,你不必担心将任务保持在范围内.但问题似乎仅限于在ThreadPool上运行的任务,然后rooted由ThreadPool进行.但是,如果我正确理解这篇MSDN博客文章,那么SO问题的建议通常不适用,因为来自TaskCompletionSource的任务并不相似rooted.
是直接使用TaskCompletionSource的唯一关注时间吗?
但是,在使用API时,您不知道任务的来源.那么你是否需要担心存储对continuation的引用,以防提供Task来自一个TaskCompletionSource或其他一些非根源?
从需要考虑任务是否已植根(是否同步的异步I/O任务?),这似乎很快变得不方便和复杂.我很难找到关于主题的信息,但它是一个很受欢迎的库,我觉得我不应该阅读反编译的源代码来确定我是否需要担心垃圾收集器的竞争条件,所以我想我必须是遗失或误解的东西.
我在整个代码中使用async/await模式.但是,有一个API使用基于事件的异步模式.我已经阅读了MSDN和几个StackOverflow的答案,这样做的方法是使用TaskCompletionSource.
我的代码:
public static Task<string> Process(Stream data)
{
var client = new ServiceClient();
var tcs = new TaskCompletionSource<string>();
client.OnResult += (sender, e) =>
{
tcs.SetResult(e.Result);
};
client.OnError += (sender, e) =>
{
tcs.SetException(new Exception(e.ErrorMessage));
};
client.Send(data);
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
并称为:
string result = await Process(data);
Run Code Online (Sandbox Code Playgroud)
或者,用于测试:
string result = Process(data).Result;
Run Code Online (Sandbox Code Playgroud)
该方法总是很快返回,但是没有一个事件被触发.
如果我添加tcs.Task.Await(); 就在return语句之前,它可以工作,但是这并不能提供我想要的异步行为.
我已经比较了我在互联网上看到的各种样本,但没有看到任何差异.
c# asynchronous task-parallel-library async-await taskcompletionsource