我有一个场景,可能非常普遍:
1. 有一个任务(一个 UI 命令处理器),可以同步或异步完成。 2. 命令可能会比它们被处理的速度更快。 3. 如果已经有一个挂起的命令任务,则应将新的命令处理程序任务排队并顺序处理。 4. 每个新任务的结果可能取决于前一个任务的结果。
取消应该被观察,但为了简单起见,我希望将其放在问题范围之外。同时,线程安全(并发)不是必需的,但必须支持重入。
下面是我尝试实现这一目标的基本示例(作为控制台应用程序,以简化操作)。
输出结果:
测试#1...
上一个任务的结果:0 这个任务的参数:1000
上一个任务的结果:1000 这个任务的参数:900
上一个任务的结果:900 这个任务的参数:800
按任意键继续进行测试#2...
上一个任务的结果:800
上一个任务的结果:800 这个任务的参数:200 这个任务的参数:100
按任意键退出...
期望的输出应为
如何让测试#2也能工作?
一种解决方法是强制对每个任务进行异步处理,使用
在实际项目中,我负责上述的
1. 有一个任务(一个 UI 命令处理器),可以同步或异步完成。 2. 命令可能会比它们被处理的速度更快。 3. 如果已经有一个挂起的命令任务,则应将新的命令处理程序任务排队并顺序处理。 4. 每个新任务的结果可能取决于前一个任务的结果。
取消应该被观察,但为了简单起见,我希望将其放在问题范围之外。同时,线程安全(并发)不是必需的,但必须支持重入。
下面是我尝试实现这一目标的基本示例(作为控制台应用程序,以简化操作)。
using System;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var asyncOp = new AsyncOp<int>();
Func<int, Task<int>> handleAsync = async (arg) =>
{
Console.WriteLine("this task arg: " + arg);
//await Task.Delay(arg); // make it async
return await Task.FromResult(arg); // sync
};
Console.WriteLine("Test #1...");
asyncOp.RunAsync(() => handleAsync(1000));
asyncOp.RunAsync(() => handleAsync(900));
asyncOp.RunAsync(() => handleAsync(800));
asyncOp.CurrentTask.Wait();
Console.WriteLine("\nPress any key to continue to test #2...");
Console.ReadLine();
asyncOp.RunAsync(() =>
{
asyncOp.RunAsync(() => handleAsync(200));
return handleAsync(100);
});
asyncOp.CurrentTask.Wait();
Console.WriteLine("\nPress any key to exit...");
Console.ReadLine();
}
// AsyncOp
class AsyncOp<T>
{
Task<T> _pending = Task.FromResult(default(T));
public Task<T> CurrentTask { get { return _pending; } }
public Task<T> RunAsync(Func<Task<T>> handler)
{
var pending = _pending;
Func<Task<T>> wrapper = async () =>
{
// await the prev task
var prevResult = await pending;
Console.WriteLine("\nprev task result: " + prevResult);
// start and await the handler
return await handler();
};
_pending = wrapper();
return _pending;
}
}
}
}
输出结果:
测试#1...
上一个任务的结果:0 这个任务的参数:1000
上一个任务的结果:1000 这个任务的参数:900
上一个任务的结果:900 这个任务的参数:800
按任意键继续进行测试#2...
上一个任务的结果:800
上一个任务的结果:800 这个任务的参数:200 这个任务的参数:100
按任意键退出...
asyncOp.RunAsync(() =>
{
asyncOp.RunAsync(() => handleAsync(200));
return handleAsync(100);
});
期望的输出应为
100
,200
,而不是200
,100
,这是因为已经有一个挂起的外部任务100
。这显然是因为内部任务同步执行,破坏了逻辑var pending = _pending; /*...*/_pending = wrapper()
的外部任务。如何让测试#2也能工作?
一种解决方法是强制对每个任务进行异步处理,使用
Task.Factory.StartNew(…,TaskScheduler.FromCurrentSynchronizationContext()
。然而,我不想强制要求命令处理程序在内部是同步执行的。此外,我不想依赖于任何特定的同步上下文的行为(即依赖于Task.Factory.StartNew
应该在创建的任务实际启动之前返回)。在实际项目中,我负责上述的
AsyncOp
,但无法控制命令处理程序(即handleAsync
中的任何内容)。