【发布时间】:2020-12-03 19:17:16
【问题描述】:
我正在研究一个协议,并尝试尽可能多地使用 async/await 以使其能够很好地扩展。该协议必须支持成百上千的同时连接。下面是一点点伪代码来说明我的问题。
private static async void DoSomeWork()
{
var protocol = new FooProtocol();
await protocol.Connect("127.0.0.1", 1234);
var i = 0;
while(i != int.MaxValue)
{
i++;
var request = new FooRequest();
request.Payload = "Request Nr " + i;
var task = protocol.Send(request);
_ = task.ContinueWith(async tmp =>
{
var resp = await task;
Console.WriteLine($"Request {resp.SequenceNr} Successful: {(resp.Status == 0)}");
});
}
}
下面是协议的一些伪代码。
public class FooProtocol
{
private int sequenceNr = 0;
private SemaphoreSlim ss = new SemaphoreSlim(20, 20);
public Task<FooResponse> Send(FooRequest fooRequest)
{
var tcs = new TaskCompletionSource<FooResponse>();
ss.Wait();
var tmp = Interlocked.Increment(ref sequenceNr);
fooRequest.SequenceNr = tmp;
// Faking some arbitrary delay. This work is done over sockets.
Task.Run(async () =>
{
await Task.Delay(1000);
tcs.SetResult(new FooResponse() {SequenceNr = tmp});
ss.Release();
});
return tcs.Task;
}
}
我有一个包含请求和响应对的协议。我使用过异步套接字编程。 FooProtocol 将负责将请求与响应(序列号)进行匹配,并且还将负责挂起请求的最大数量。 (在伪和我的代码中使用信号量苗条完成,所以我不担心请求失控)。 DoSomeWork 方法调用 Protocol.Send 方法,但我不想等待响应,我想旋转并发送下一个,直到我被最大数量的未决请求阻塞。当任务完成后,我想检查响应并可能做一些工作。
我想解决两件事
- 我想避免使用 Task.ContinueWith(),因为它似乎不完全符合 async/await 模式
- 因为我一直在等待连接,所以我不得不使用 async 修饰符。现在我收到来自 IDE 的警告“因为不等待此调用,所以在此调用完成之前继续执行当前方法。考虑将 'await' 运算符应用于调用结果。”我不想这样做,因为一旦我这样做,它就会破坏协议处理许多请求的能力。我可以摆脱警告的唯一方法是使用丢弃。这不是最糟糕的事情,但我不禁觉得我错过了一个技巧,并且太努力了。
【问题讨论】:
-
您可以将作业委托给线程池并忘记它 ->
_ = Task.Run(async () => { var resp = await protocol.Send(request); Console.WriteLine($"Request {resp.SequenceNr} Successful: {(resp.Status == 0)}"); } -
@CamiloTerevinto 听起来有成百上千的并发请求,你会饿死线程池。除此之外,它有点违背异步的目的。
-
@GSerg 是的,我担心会是这样。也许只是推送发送请求并获得对
async void方法的响应? -
将
await protocol.Send(request); Console.WriteLine分解为单独的Func<Task>,继续创建它们并将它们添加到列表中,然后最后对它们执行Task.WhenAll?再说一次,你可能想要实现某种throttling。 -
@GSerg Throttling 内置于协议中。我把它省略了伪代码。我无法保留所有任务的列表,因为这些连接是长期存在的,可能在服务的生命周期内。
标签: c# asynchronous concurrency