如何形成 TaskCompletionSource<T> 的集合并保持类型安全
How to form collection of TaskCompletionSource<T> and keep type safety
我有一些异步方法:
// not ideal
private TaskCompletionSource<A> _tcsA;
private TaskCompletionSource<A> _tcsB;
private TaskCompletionSource<A> _tcsC;
...
public Task<A> GetAAsync() {
_currentTask = TaskType.A;
_tcsA = new TaskCompletionSource<A>();
// some complex non-sync task, using http/events that ends with Complete();
// QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
return _tcsA.Task;
}
public Task<B> GetBAsync() {
_currentTask = TaskType.B;
_tcsB = new TaskCompletionSource<B>();
// some complex non-sync task, using http/events that ends with Complete();
// QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
return _tcsB.Task;
}
public Task<C> GetCAsync() {
_currentTask = TaskType.C;
_tcsC = new TaskCompletionSource<C>();
// some complex non-sync task, using http/events that ends with Complete();
// QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
return _tcsC.Task;
}
// called by an external source, a http listener / processor
// this should complete the async call by the client and return results
public void Complete(Result result) {
switch (_currentTask) {
case TaskType.A:
_tcsA.SetResult(new A());
break;
case TaskType.B:
_tcsB.SetResult(new B());
break;
case TaskType.C:
_tcsC.SetResult(new C());
break;
}
_currentTask = TaskType.None;
}
为了简单起见,以上是半伪代码。我这样调用其中一种方法:
A a = await service.GetAAsync();
现在的问题是 TaskCompletionSource<T>
是通用的,如果我有 100 个这样的方法,我将不得不为每个 return 类型创建一个变量。但是由于 一次只能调用一个 方法,所以使用单个 TaskCompletionSource
会很好,但不要将其键入 object (TaskCompletionSource<object>)
.
我不想做:
object a = await service.GetAAsync();
因为这需要客户投射。所以最好的解决方案是有一个 TaskCompletionSource
,但以某种方式输入它。或者也可以拥有 TaskCompletionSource
的字典。这两个对我来说似乎都是不可能的。
我该如何解决?
更新:
有关我的情况的背景,请查看:
I don't want to do object a = await service.GetAAsync()
because that will need casting by the client.
不一定。保持相同的签名,但添加对 ContinueWith
的调用以转换为适当的类型:
public Task<A> GetAAsync() {
_currentTask = TaskType.A;
_tcs = new TaskCompletionSource<object>();
// some complex task that ends with Complete();
return _tcs.Task.ContinueWith(t => (A)t.Result);
}
或者您可以使用 async/await:
做同样的事情
public async Task<A> GetAAsync() {
_currentTask = TaskType.A;
_tcs = new TaskCompletionSource<object>();
// some complex task that ends with Complete();
return (A)await _tcs.Task;
}
这很容易用 TaskCompletionSourceDictionary <TKey>
:
目标:
- 任何类型都可以用作密钥 - 我在此示例中使用了您的
TaskType
枚举
- 如果每种类型只有一个实例,则有一个更简单的非通用版本
- 生产者和消费者的动态数量
- 结果的完成可以被多个消费者观察到
- 强制执行类型安全 - 生产者和消费者必须指定相同的类型,否则转换异常会描述类型差异
- 生产者可以在消费者请求任务之前设置结果
- 消费者可以在生产者设置结果之前请求任务
我们可以在这个新 class:
上使用 GetOrAdd
方法完成所有这些
TaskCompletionSource<TValue> GetOrAdd<TValue>(TKey key)
用法示例:
private readonly TaskCompletionSourceDictionary<TaskType> _tasks = new TaskCompletionSourceDictionary<TaskType>();
public Task<A> GetAAsync() =>
_tasks.GetOrAdd<A>(TaskType.A).Task;
public Task<B> GetBAsync() =>
_tasks.GetOrAdd<B>(TaskType.B).Task;
public Task<C> GetCAsync() =>
_tasks.GetOrAdd<C>(TaskType.C).Task;
// called by an external source, a http listener / processor
// this should complete the async call by the client and return results
public void Complete(Result result)
{
switch (_currentTask)
{
case TaskType.A:
_tasks.GetOrAdd<A>(TaskType.A).TrySetResult(new A());
break;
case TaskType.B:
_tasks.GetOrAdd<B>(TaskType.B).TrySetResult(new B());
break;
case TaskType.C:
_tasks.GetOrAdd<C>(TaskType.C).TrySetResult(new C());
break;
}
_currentTask = TaskType.None;
}
除了GetOrAdd
,还有TryAdd
和TryRemove
。尽管删除可能很危险 - 为最后一个消费者删除。
我有一些异步方法:
// not ideal
private TaskCompletionSource<A> _tcsA;
private TaskCompletionSource<A> _tcsB;
private TaskCompletionSource<A> _tcsC;
...
public Task<A> GetAAsync() {
_currentTask = TaskType.A;
_tcsA = new TaskCompletionSource<A>();
// some complex non-sync task, using http/events that ends with Complete();
// QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
return _tcsA.Task;
}
public Task<B> GetBAsync() {
_currentTask = TaskType.B;
_tcsB = new TaskCompletionSource<B>();
// some complex non-sync task, using http/events that ends with Complete();
// QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
return _tcsB.Task;
}
public Task<C> GetCAsync() {
_currentTask = TaskType.C;
_tcsC = new TaskCompletionSource<C>();
// some complex non-sync task, using http/events that ends with Complete();
// QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
return _tcsC.Task;
}
// called by an external source, a http listener / processor
// this should complete the async call by the client and return results
public void Complete(Result result) {
switch (_currentTask) {
case TaskType.A:
_tcsA.SetResult(new A());
break;
case TaskType.B:
_tcsB.SetResult(new B());
break;
case TaskType.C:
_tcsC.SetResult(new C());
break;
}
_currentTask = TaskType.None;
}
为了简单起见,以上是半伪代码。我这样调用其中一种方法:
A a = await service.GetAAsync();
现在的问题是 TaskCompletionSource<T>
是通用的,如果我有 100 个这样的方法,我将不得不为每个 return 类型创建一个变量。但是由于 一次只能调用一个 方法,所以使用单个 TaskCompletionSource
会很好,但不要将其键入 object (TaskCompletionSource<object>)
.
我不想做:
object a = await service.GetAAsync();
因为这需要客户投射。所以最好的解决方案是有一个 TaskCompletionSource
,但以某种方式输入它。或者也可以拥有 TaskCompletionSource
的字典。这两个对我来说似乎都是不可能的。
我该如何解决?
更新:
有关我的情况的背景,请查看:
I don't want to do
object a = await service.GetAAsync()
because that will need casting by the client.
不一定。保持相同的签名,但添加对 ContinueWith
的调用以转换为适当的类型:
public Task<A> GetAAsync() {
_currentTask = TaskType.A;
_tcs = new TaskCompletionSource<object>();
// some complex task that ends with Complete();
return _tcs.Task.ContinueWith(t => (A)t.Result);
}
或者您可以使用 async/await:
做同样的事情public async Task<A> GetAAsync() {
_currentTask = TaskType.A;
_tcs = new TaskCompletionSource<object>();
// some complex task that ends with Complete();
return (A)await _tcs.Task;
}
这很容易用 TaskCompletionSourceDictionary <TKey>
:
目标:
- 任何类型都可以用作密钥 - 我在此示例中使用了您的
TaskType
枚举 - 如果每种类型只有一个实例,则有一个更简单的非通用版本
- 生产者和消费者的动态数量
- 结果的完成可以被多个消费者观察到
- 强制执行类型安全 - 生产者和消费者必须指定相同的类型,否则转换异常会描述类型差异
- 生产者可以在消费者请求任务之前设置结果
- 消费者可以在生产者设置结果之前请求任务
我们可以在这个新 class:
上使用GetOrAdd
方法完成所有这些
TaskCompletionSource<TValue> GetOrAdd<TValue>(TKey key)
用法示例:
private readonly TaskCompletionSourceDictionary<TaskType> _tasks = new TaskCompletionSourceDictionary<TaskType>();
public Task<A> GetAAsync() =>
_tasks.GetOrAdd<A>(TaskType.A).Task;
public Task<B> GetBAsync() =>
_tasks.GetOrAdd<B>(TaskType.B).Task;
public Task<C> GetCAsync() =>
_tasks.GetOrAdd<C>(TaskType.C).Task;
// called by an external source, a http listener / processor
// this should complete the async call by the client and return results
public void Complete(Result result)
{
switch (_currentTask)
{
case TaskType.A:
_tasks.GetOrAdd<A>(TaskType.A).TrySetResult(new A());
break;
case TaskType.B:
_tasks.GetOrAdd<B>(TaskType.B).TrySetResult(new B());
break;
case TaskType.C:
_tasks.GetOrAdd<C>(TaskType.C).TrySetResult(new C());
break;
}
_currentTask = TaskType.None;
}
除了GetOrAdd
,还有TryAdd
和TryRemove
。尽管删除可能很危险 - 为最后一个消费者删除。