1

編集:説明をクリーンアップしています。これは...WriteAsyncだけでなく、これも影響を与えると判断したためです。ReadAsync

これらの呼び出しの 1 つが現在ブロックReadAsyncされている場合 (チャネルが空であるかWriteAsync、チャネルがいっぱいであるために)、キャンセル トークンを通知しても呼び出し元に実行が返されることはありません。つまり、値を返さず、スローもしません。永遠にブロックするだけです。Complete別のスレッドからチャネルを呼び出すと、ブロックされた呼び出しが throwChannelClosedExceptionになりますが、通知されたキャンセル トークンが十分でない理由は明確ではありません。

さらに混乱を招くことに、コードは実際には .NET Fiddle として期待どおりに機能しますが、Visual Studio 2019 内またはコマンド プロンプト (両方とも Windows 10 x64) からは機能しません。

以下のサンプル コードではComplete、main の行のコメントを解除すると、クリーン シャットダウンが可能になりますが、そうしないと、呼び出しがWriteAsync返されないため、呼び出しがTask.WaitAll返されません。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;


public class Program
{
    public static async Task Task1(Channel<String> q1, CancellationToken cancellationToken)
    {
        int idx = 0;

        Console.WriteLine($"Task1 starting");

        while (!cancellationToken.IsCancellationRequested)
        {
            try
            {
                await Task.Delay(100);

                string s = $"element{idx++}";

                Console.WriteLine($"Calling write on {s}");
                await q1.Writer.WriteAsync(s, cancellationToken);
                Console.WriteLine($"Write returned for {s}");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine($"Operation was cancelled");
            }
            catch (ChannelClosedException)
            {
                Console.WriteLine($"Channel was closed");
            }
        }

        //q1.Writer.Complete();
        Console.WriteLine($"Task1 stopping");

    }

    public static async Task Main()
    {
        Console.WriteLine($"Main started");

        var tasklist = new List<Task>();

        var q1 = Channel.CreateBounded<String>(
            new BoundedChannelOptions(10)
            {
                AllowSynchronousContinuations = false,
                SingleReader = true,
                SingleWriter = true,
                FullMode = BoundedChannelFullMode.Wait
            });

        var cts = new CancellationTokenSource(5000);

        tasklist.Add(Task.Run(() => Task1(q1, cts.Token), cts.Token));

        while (!cts.Token.IsCancellationRequested)
        {
            try
            {
                await Task.Delay(10000, cts.Token);
            }
            catch (OperationCanceledException) { }
        }

        //q1.Writer.Complete();

        Console.WriteLine($"Waiting for all tasks to terminate");
        Task.WaitAll(tasklist.ToArray(), CancellationToken.None);
        Console.WriteLine($"All tasks terminated");
    }
}

そして出力:

Main started
Task1 starting
Calling write on element0
Write returned for element0
Calling write on element1
Write returned for element1
Calling write on element2
Write returned for element2
Calling write on element3
Write returned for element3
Calling write on element4
Write returned for element4
Calling write on element5
Write returned for element5
Calling write on element6
Write returned for element6
Calling write on element7
Write returned for element7
Calling write on element8
Write returned for element8
Calling write on element9
Write returned for element9
Calling write on element10
Waiting for all tasks to terminate
4

1 に答える 1