AFAIK,它所知道的只是在某些时候,它的SetResult或SetException方法被调用来完成通过Task属性暴露的Task<T>。
换句话说,它充当Task<TResult>及其完成的生产者。
我在这里看到了一个例子:
如果我需要一种方法来异步执行Func<T>,并有一个任务<T>
来表示这个操作。
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
如果我没有Task.Factory.StartNew -
但是我有task。factory。startnew。
问题:
有人能举例说明一个与TaskCompletionSource直接相关的场景吗
而不是假设没有task。factory。startnew ?
似乎没有人提到,但我想单元测试也可以被认为是真实的生活。
我发现TaskCompletionSource在使用异步方法模拟依赖时非常有用。
在实际被测程序中:
public interface IEntityFacade
{
Task<Entity> GetByIdAsync(string id);
}
在单元测试中:
// set up mock dependency (here with NSubstitute)
TaskCompletionSource<Entity> queryTaskDriver = new TaskCompletionSource<Entity>();
IEntityFacade entityFacade = Substitute.For<IEntityFacade>();
entityFacade.GetByIdAsync(Arg.Any<string>()).Returns(queryTaskDriver.Task);
// later on, in the "Act" phase
private void When_Task_Completes_Successfully()
{
queryTaskDriver.SetResult(someExpectedEntity);
// ...
}
private void When_Task_Gives_Error()
{
queryTaskDriver.SetException(someExpectedException);
// ...
}
毕竟,TaskCompletionSource的这种用法似乎是“不执行代码的任务对象”的另一种情况。
根据我的经验,TaskCompletionSource非常适合将旧的异步模式包装到现代的异步/await模式中。
我能想到的最有用的例子是使用Socket。它具有旧的APM和EAP模式,但没有TcpListener和TcpClient所具有的可等待的任务方法。
我个人对NetworkStream类有几个问题,更喜欢原始Socket。因为我也喜欢async/await模式,所以我做了一个扩展类SocketExtender,它为Socket创建了几个扩展方法。
所有这些方法都使用TaskCompletionSource<T>来包装异步调用,如下所示:
public static Task<Socket> AcceptAsync(this Socket socket)
{
if (socket == null)
throw new ArgumentNullException("socket");
var tcs = new TaskCompletionSource<Socket>();
socket.BeginAccept(asyncResult =>
{
try
{
var s = asyncResult.AsyncState as Socket;
var client = s.EndAccept(asyncResult);
tcs.SetResult(client);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}, socket);
return tcs.Task;
}
我将套接字传递给BeginAccept方法,这样我就可以从编译器中获得轻微的性能提升,而不必提升局部参数。
这一切的美妙之处在于:
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Loopback, 2610));
listener.Listen(10);
var client = await listener.AcceptAsync();
在这篇博文中,Levi Botelho描述了如何使用TaskCompletionSource为Process编写异步包装器,这样您就可以启动它并等待它的终止。
public static Task RunProcessAsync(string processPath)
{
var tcs = new TaskCompletionSource<object>();
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo(processPath)
{
RedirectStandardError = true,
UseShellExecute = false
}
};
process.Exited += (sender, args) =>
{
if (process.ExitCode != 0)
{
var errorMessage = process.StandardError.ReadToEnd();
tcs.SetException(new InvalidOperationException("The process did not exit correctly. " +
"The corresponding error message was: " + errorMessage));
}
else
{
tcs.SetResult(null);
}
process.Dispose();
};
process.Start();
return tcs.Task;
}
以及它的用法
await RunProcessAsync("myexecutable.exe");
TaskCompletionSource is used to create Task objects that don't execute code.
In real world scenarios, TaskCompletionSource is ideal for I/O bound operations. This way, you get all the benefits of tasks (e.g. return values, continuations, etc) without blocking a thread for the duration of the operation. If your "function" is an I/O bound operation, it isn't recommended to block a thread using a new Task. Instead, using TaskCompletionSource, you can create a slave task to just indicate when your I/O bound operation finishes or faults.