xamarin と .net 5 async/awaits に基づいて Android メッセンジャー アプリを開発しています。
私のアプリには、無限ループで作成されたメッセージを処理するためのプロデューサー/コンシューマー パターンがあります。
たとえば、ReadTcpClientAsync プロデューサー:
async Task ReadTcpClientAsync(CancellationToken cancellationToken)
{
cde.Signal();
while (!cancellationToken.IsCancellationRequested)
{
byte[] buffer = await atc.ReadAsync(cancellationToken);
// queue message...
}
}
または、メッセージをデキューして WriteAsync を待機する SendStatementsAsync コンシューマ
private async Task SendStatementsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var nextItem = await _outputStatements.Take();
cancellationToken.ThrowIfCancellationRequested();
// misc ...
await atc.WriteAsync(call.Serialize());
}
}
一部の消費者は、通話を待つだけです
var update = await _inputUpdateStatements.Take();
この構成はテストではかなりうまく機能しますが、大きな間違いを犯したと思われる方法が 1 つあります。このメソッドは、3 つの pro/con while (true) ループを同時に開始して、クライアント バックエンド全体を実行することを目的としています。
ここにあります:
public async Task RunAsync()
{
_isRunning = true;
_progress.ProgressChanged += progress_ProgressChanged;
await InitMTProto(_scheme).ConfigureAwait(false); // init smth...
// various init stuf...
await atc.ConnectAsync().ConfigureAwait(false); // open connection async
// IS IT WRONG?
try
{
await Task.WhenAny(SendStatementsAsync(_cts.Token),
ReadTcpClientAsync(_cts.Token),
ProcessUpdateAsync(_cts.Token, _progress)).ConfigureAwait(false);
}
catch (OperationCanceledException oce)
{
}
catch (Exception ex)
{
}
}
今のところ Android のことは忘れて、UI コンテキストで任意の UI (WinForm、WPF など) の OnCreate メソッドを呼び出して RunAsync を呼び出すと考えてください。
protected async override void OnCreate(Bundle bundle)
{
// start RA
await client.RunAsync()
// never gets here - BAD, but nonblock UI thread - good
Debug.WriteLine("nevar");
}
ご覧のとおり、問題があります。Task.WhenAny(...) から返されることはないため、RunAsync await 呼び出しの後は何もできません。そこでステータスチェックを実行する必要がありますが、この賛否両論メソッドを開始する必要があります。
if (!cde.Wait(15000))
{
throw new TimeoutException("Init too long");
}
また、私のチェックも非同期で、魅力的に機能します:)
public async Task<TLCombinatorInstance> PerformRpcCall(string combinatorName, params object[] pars)
{
// wait for init on cde ...
// prepare call ...
// Produce
ProduceOutput(call);
// wait for answer
return await _inputRpcAnswersStatements.Take();
}
この無限ループを開始するには別のアプローチを使用する必要があると思いますが、すでに async Task メソッドをずっと持っているので、何をすべきかわかりません。何か助けてください。