简体   繁体   中英

Channel Writer requiring Task.Delay and not processing all messages

I am adding messages to a threading channel and reading from this channel constantly. I noticed that if I do not add a Task.Delay then all of the messages are not processed. The program will exit with maybe 10 messages processed when it should be 1000.

Adding a Task.Delay after each write seems hacky. Is there a better way to read all messages in the channel without adding a Task.Delay after each write?

Is there something wrong with my StartListener() method?

internal class Program
{
    static List<Task> taskList = new List<Task>();
    private static Channel<string> messageList = Channel.CreateUnbounded<string>();
    static int midpointCount = 0;
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        Task.WhenAll(Task.Run(() => StartListener()));
        for (int i = 0; i < 10; i++)
        {
            int task = i;
            taskList.Add(Task.Run(() => StartJob(task)));
        }            
        Task.WaitAll(taskList.ToArray());
        sw.Stop();
        Console.WriteLine("Total Messages Processed: {0} in time {1} MessageListCount {2}", midpointCount, sw.Elapsed, messageList.Reader.Count);
    }

    private static async Task<string> StartListener()
    {
        var cancellationtoken = new CancellationTokenSource(TimeSpan.FromMinutes(60)).Token;
        await foreach (var msg in messageList.Reader.ReadAllAsync(cancellationtoken))
        {
            if (!string.IsNullOrEmpty(msg))
            {
                Console.WriteLine(msg);
                Interlocked.Increment(ref midpointCount);
            }
        }           
        return "Finished";
    }

    private static async Task<string> StartJob(int TaskNum)
    {
        Random rnd = new Random();
        for (int i = 0; i < 100; i++)
        {
            var cancellationtoken = new CancellationTokenSource(TimeSpan.FromMinutes(60)).Token;
            try
            {                    
                var message = string.Format("TaskNum {0} Message added #{1}", TaskNum, rnd.Next(1, 3000));
                await messageList.Writer.WriteAsync(message);
                await Task.Delay(50);  //<--- Here it seems it will only read all messages with a delay involved.
            }
            catch (OperationCanceledException)
            {
                // ignored
            }
        }           
        return "Finished";
    }
}
Task.WhenAll(Task.Run(() => StartListener()));

StartListener returns a Task . You wrap that in Task.Run , starting another thread to run that task. You then pass than task to the Task.WhenAll method , which returns a Task that you promptly throw away.

The only tasks you add to the taskList variable are the StartJob tasks. Your Main method will finish as soon as all of the StartJob tasks have finished. It will not wait for the StartListener task to finish.

Change your code to wait for the listener to finish.

static void Main(string[] args)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    
    taskList.Add(Task.Run(() => StartListener()));
    
    for (int i = 0; i < 10; i++)
    {
        int task = i;
        taskList.Add(Task.Run(() => StartJob(task)));
    }

     Task.WaitAll(taskList.ToArray());
     sw.Stop();
     
     Console.WriteLine("Total Messages Processed: {0} in time {1} MessageListCount {2}", 
        midpointCount, sw.Elapsed, messageList.Reader.Count);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM