我有一些库(套接字网络)代码,它Task基于提供基于API的待处理请求响应TaskCompletionSource<T>.然而,TPL中的一个烦恼是,似乎不可能阻止同步延续.我会希望能够做的是两种:
TaskCompletionSource<T>不应该允许呼叫者附加TaskContinuationOptions.ExecuteSynchronously,或SetResult/ TrySetResult)以指定TaskContinuationOptions.ExecuteSynchronously应该忽略的方式具体来说,我遇到的问题是传入的数据正在由专用的阅读器处理,如果调用者可以附加,TaskContinuationOptions.ExecuteSynchronously他们可以阻止阅读器(这不仅影响它们).以前,我通过一些hackery解决了这个问题,它检测是否存在任何延续,如果它们将完成推送到ThreadPool,那么如果调用者已经使工作队列饱和,则会产生重大影响,因为完成将不会被处理及时.如果他们使用Task.Wait()(或类似),他们将基本上陷入僵局.同样,这就是读者使用专用线程而不是使用工作者的原因.
所以; 在我尝试唠叨TPL团队之前:我错过了一个选项吗?
关键点:
ThreadPool作为一个实现,因为它需要在池饱和时工作以下示例生成输出(排序可能因时间而异):
Continuation on: Main thread
Press [return]
Continuation on: Thread pool
Run Code Online (Sandbox Code Playgroud)
问题在于随机调用者设法在"主线程"上获得延续.在实际代码中,这将打断主要读者; 坏事!
码:
using System;
using System.Threading;
using System.Threading.Tasks;
static class Program
{
static void Identify()
{
var thread = Thread.CurrentThread;
string name = thread.IsThreadPoolThread
? "Thread pool" : thread.Name;
if (string.IsNullOrEmpty(name))
name = …Run Code Online (Sandbox Code Playgroud) 我对await关键字的工作方式有一个脆弱的把握,我想稍微扩展一下我对它的理解.
仍然让我头疼的问题是使用递归.这是一个例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestingAwaitOverflow
{
class Program
{
static void Main(string[] args)
{
var task = TestAsync(0);
System.Threading.Thread.Sleep(100000);
}
static async Task TestAsync(int count)
{
Console.WriteLine(count);
await TestAsync(count + 1);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这显然是一个StackOverflowException.
我的理解是因为代码实际上是同步运行的,直到第一个异步操作,之后它返回一个Task包含异步操作信息的对象.在这种情况下,没有异步操作,因此它只是在最终得到Task返回的错误承诺下继续递归.
现在改变它只是一点点:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestingAwaitOverflow
{
class Program
{
static void Main(string[] args)
{
var task = TestAsync(0);
System.Threading.Thread.Sleep(100000); …Run Code Online (Sandbox Code Playgroud) 如果我需要推迟代码执行,直到UI线程消息循环的未来迭代之后,我可以这样做:
await Task.Factory.StartNew(
() => {
MessageBox.Show("Hello!");
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
这将类似于await Task.Yield(); MessageBox.Show("Hello!");,除了我有一个选项可以取消任务,如果我想.
在使用默认同步上下文的情况下,我可以类似地使用await Task.Run继续池线程.
事实上,我喜欢Task.Factory.StartNew和Task.Run更多Task.Yield,因为他们都明确定义了延续代码的范围.
那么,在什么情况下await Task.Yield()实际上有用呢?
我在Windows服务中有一个计时器,并且在timer_Elapsed事件处理程序中调用了异步方法:
protected override void OnStart(string[] args)
{
timer.Start();
}
private async void timer_Elapsed(object sender, ElapsedEventArgs e)
{
_timer.Stop();
await DoSomething();
_timer.Start();
}
Run Code Online (Sandbox Code Playgroud)
上面的代码好吗?首先,我读过async void不是一个好主意.其次,为什么我需要timer_Elapsed方法首先异步?它不像我们将由多个调用者并行调用的已用事件处理程序.但是,如果我不使方法异步,那么我的逻辑将会中断,因为计时器将在DoSomething完成之前启动.
那么,正确的方法是使timer_Elapsed异步吗?
我有一个用例,我需要:
输入看起来像这样:
<Root>
<Input>
<Case>ABC123</Case>
<State>MA</State>
<Investor>Goldman</Investor>
</Input>
<Input>
<Case>BCD234</Case>
<State>CA</State>
<Investor>Goldman</Investor>
</Input>
</Root>
Run Code Online (Sandbox Code Playgroud)
和输出:
<Results>
<Output>
<Case>ABC123</Case>
<State>MA</State>
<Investor>Goldman</Investor>
<Price>75.00</Price>
<Product>Blah</Product>
</Output>
<Output>
<Case>BCD234</Case>
<State>CA</State>
<Investor>Goldman</Investor>
<Price>55.00</Price>
<Product>Ack</Product>
</Output>
</Results>
Run Code Online (Sandbox Code Playgroud)
我想并行运行计算; 典型的输入文件可能有50,000个输入节点,没有线程的总处理时间可能是90分钟.大约90%的处理时间花在步骤#2(计算)上.
static IEnumerable<XElement> EnumerateAxis(XmlReader reader, string axis)
{
reader.MoveToContent();
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.Name == axis)
{
XElement el = XElement.ReadFrom(reader) as XElement;
if (el != null)
yield return el;
}
break;
}
}
} …Run Code Online (Sandbox Code Playgroud)