AFAIK,所有它知道的是,在一些点,其SetResult
或SetException
方法被调用来完成Task<T>
通过其暴露的Task
属性。
换句话说,它作为生产者对一个Task<TResult>
和它的完成。
我看到这里的例子:
如果我需要一种方法来异步执行函数求,并有一个任务来表示操作。
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
?
Answer 1:
我主要使用它时,只有基于事件的API是可用的( 例如Windows Phone的8座 ):
public Task<Args> SomeApiWrapper()
{
TaskCompletionSource<Args> tcs = new TaskCompletionSource<Args>();
var obj = new SomeApi();
// will get raised, when the work is done
obj.Done += (args) =>
{
// this will notify the caller
// of the SomeApiWrapper that
// the task just completed
tcs.SetResult(args);
}
// start the work
obj.Do();
return tcs.Task;
}
所以与C#5一起使用时,它是特别有用的async
关键字。
Answer 2:
在我的经验, TaskCompletionSource
是伟大的包装旧异步模式,以现代async/await
模式。
最有利的例子,我能想到的是工作时Socket
。 它有老APM和EAP模式,但不是awaitable Task
中的方法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;
}
我通过socket
进入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();
Answer 3:
对我来说,使用经典的场景TaskCompletionSource
是当它可能是我的方法不一定会做一个耗时的操作。 什么它允许我们做的是选择特定的情况下,我们想用一个新的线程。
这方面的一个很好的例子是,当你使用一个缓存。 你可以有一个GetResourceAsync
方法,它看起来在缓存中所请求的资源,并立即返回(不使用一个新的线程,通过使用TaskCompletionSource
)如果资源被发现。 只有没有被发现的资源,我们想用一个新的线程,并使用检索Task.Run()
一个代码示例可以看这里: 如何有条件地运行代码asynchonously使用任务
Answer 4:
在这篇博客文章 ,列维·博特略介绍了如何使用TaskCompletionSource
写一个过程,这样你可以启动它,并等待其终止的异步包装。
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");
Answer 5:
TaskCompletionSource用于创建不执行代码任务的对象。 在实际场景TaskCompletionSource是理想的I / O密集型操作。 这样,你得到所有的任务(如返回值,延续等)的好处,而不阻塞线程操作的持续时间。 如果你的“功能”是一个IO约束操作不建议阻止使用新任务的线程。 而不是使用TaskCompletionSource你可以创建一个从机任务,只是表示当你的I / O密集型操作完成或故障。
Answer 6:
它看起来像没有提到任何一个,但我想单元测试也可以被认为是真实的生活就够了。
我觉得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的这种用法似乎“不执行代码任务对象”的另一种情况。
Answer 7:
有与本像样的解释现实世界的例子从博客“与.NET并行编程”的帖子 。 你真的应该读它,但这里有一个总结呢。
博客文章显示了两种实现方式为:
延迟“用于创建一个工厂方法‘’任务,那些实际上不会被调度,直到发生了某些用户提供超时。”
所示的第一实施基于Task<>
和具有两个主要缺陷。 第二实施后接着通过使用以减轻这些TaskCompletionSource<>
下面是第二次执行:
public static Task StartNewDelayed(int millisecondsDelay, Action action)
{
// Validate arguments
if (millisecondsDelay < 0)
throw new ArgumentOutOfRangeException("millisecondsDelay");
if (action == null) throw new ArgumentNullException("action");
// Create a trigger used to start the task
var tcs = new TaskCompletionSource<object>();
// Start a timer that will trigger it
var timer = new Timer(
_ => tcs.SetResult(null), null, millisecondsDelay, Timeout.Infinite);
// Create and return a task that will be scheduled when the trigger fires.
return tcs.Task.ContinueWith(_ =>
{
timer.Dispose();
action();
});
}
Answer 8:
这里我用我真实的场景TaskCompletionSource
是实现下载队列时。 在我的情况下,如果用户开始下载100我不想解雇他们全部关闭一次等,而不是返回strated任务的我回到附加到任务TaskCompletionSource
。 一旦下载完成后得到该工作队列线程完成任务。
这里的关键概念是,我去耦当客户端请求一个任务从当它实际上被开始启动。 在这种情况下,因为我不希望客户端必须处理资源管理。
请注意,您可以根据您使用的是C#编译器5(VS 2012+),只要使用异步/地等待着.NET 4看到此处了解更多详情。
Answer 9:
这可能是过于简单化的东西,但TaskCompletion源允许一个等待一个事件。 因为一旦事件发生tcs.SetResult只能设置,调用者可以等待的任务。
观看此视频中更多的内幕:
http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Lucian03-TipsForAsyncThreadsAndDatabinding
Answer 10:
我用TaskCompletionSource
直到它被取消运行任务。 在这种情况下,它是一个ServiceBus用户,我通常要为只要在应用程序运行运行。
public async Task RunUntilCancellation(
CancellationToken cancellationToken,
Func<Task> onCancel)
{
var doneReceiving = new TaskCompletionSource<bool>();
cancellationToken.Register(
async () =>
{
await onCancel();
doneReceiving.SetResult(true); // Signal to quit message listener
});
await doneReceiving.Task.ConfigureAwait(false); // Listen until quit signal is received.
}
文章来源: When should TaskCompletionSource be used?