0

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";
    }
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Terrance Jackson
  • 606
  • 3
  • 13
  • 40
  • you are ignoring the Operation cancelation. You would probably see what is happening if you didnt do that. I can imagine it complains about not beeing able to WriteAsync for watever reason – Denis Schaf Jan 17 '22 at 15:53

1 Answers1

2
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);
}
Richard Deeming
  • 29,830
  • 10
  • 79
  • 151