Tim*_*ong 10 c# asynchronous task .net-3.5 task-parallel-library
我有一些使用的代码Task<T>
推迟从串行读取操作返回结果很短的时间,如下所示:
void ReturnResponseAfterAShortDelay()
{
if (delayedResponseCancellationTokenSource != null)
delayedResponseCancellationTokenSource.Cancel(); // Cancel any pending operations and start a new one.
delayedResponseCancellationTokenSource = new CancellationTokenSource();
log.InfoFormat("Deferring response for {0} ms", Settings.Default.TimeoutMs);
Task.Delay(Properties.Settings.Default.TimeoutMs, delayedResponseCancellationTokenSource.Token)
.ContinueWith((continuation) => ReturnWhateverHasArrived(), TaskContinuationOptions.NotOnCanceled)
.Start();
}
Run Code Online (Sandbox Code Playgroud)
此代码背后的想法是在没有新字符到达指定间隔时返回结果.
但是,由于我无法控制的因素,我必须使用.NET 3.5,这会阻止我使用Task<T>
,因此我必须以某种方式重构此代码.
如何在不使用的情况下实现相同的结果Task<T>
?
虽然我展示的具体代码恰好是一个定时延迟,但我的用法并不仅限于延迟.可能还有其他情况我想立即开始一些"长时间运行"的轮询任务.典型的情况是I/O绑定操作,例如,定期查询连接到串行端口的设备,然后在满足某些条件时引发事件.
Sam*_*ell 18
您可以使用NuGet提供的以下任一软件包:
您可以在NuGet上使用TaskParallelLibrary包.此程序包具有强大的名称,是.NET 4的.NET任务并行库的.NET 3.5后端程序,包含在Reactive Extensions中.
您可以在NuGet上使用System.Threading.Tasks.Unofficial包.此替代实现使用Mono代码库而不是Microsoft实现.请注意,此程序包中包含的程序集没有强名称,因此如果您的库使用强名称,则此选项不可用.
虽然我展示的具体代码恰好是定时延迟,但我的用法并不限于延迟事物。可能还有其他情况,我想立即启动一些“长时间运行”的轮询任务。典型的情况是 I/O 绑定操作,例如定期查询连接到串行端口的设备,然后在满足某些条件时引发事件。
使用 C# 2.0 - 4.0 编码此类场景的一个值得注意且方便的技巧是使用自驱动IEnumerable
和yield
. 它允许实现异步状态机,类似于async/await
C# 5.0。这样,您就可以为异步逻辑保留方便的线性代码流。所有 C# 语言代码控制语句都可以工作(除了不能yield return
从内部执行try/catch
)。
例如,带有计时器的控制台应用程序:
using System;
using System.Collections;
using System.Threading;
namespace ConsoleApplication_22516303
{
class Program
{
class AsyncLogic
{
public EventHandler Completed = delegate { };
IEnumerable WorkAsync(Action nextStep)
{
using (var timer = new System.Threading.Timer(_ => nextStep()))
{
timer.Change(0, 500);
var tick = 0;
while (tick < 10)
{
// resume upon next timer tick
yield return Type.Missing;
Console.WriteLine("Tick: " + tick++);
}
}
this.Completed(this, EventArgs.Empty);
}
public void Start()
{
IEnumerator enumerator = null;
Action nextStep = () => enumerator.MoveNext();
enumerator = WorkAsync(nextStep).GetEnumerator();
nextStep();
}
}
static void Main(string[] args)
{
var mre = new ManualResetEvent(false);
var asyncLogic = new AsyncLogic();
asyncLogic.Completed += (s, e) => mre.Set();
asyncLogic.Start();
mre.WaitOne();
Console.WriteLine("Completed, press Enter to exit");
Console.ReadLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
任何事件都可以用一个处理程序来包装,该处理程序将调用nextStep
,类似于上面的计时器回调。yield return
该代码将在相应的, 事件发生后继续。
有相当多的实现利用了这种方法,例如Jeffrey Richter 的AsyncEnumerator
.