Efgrafich
Efgrafich

Reputation: 27

C# launching task with non-async function inside

Basic overview: program should launch task to parse some array of data and occasionally enqueue tasks to process it one at a time. Test rig have a button an two labels to display debug info. TaskQueue is a class for SemaphoreSlim from this thread

Dispatcher dispath = Application.Current.Dispatcher;

async void Test_Click(s, e)
{
   TaskQueue queue = new TaskQueue();

   // Blocks thread if SimulateParse does not have await inside
   await SimulateParse(queue);

   //await Task.Run(() => SimulateParse(queue));

   lblStatus2.Content = string.Format("Awaiting queue"));
   await queue.WaitAsync(); //this is just SemaphoreSlim.WaitAsync() 

   lblStatus.Content = string.Format("Ready"));
   lblStatus2.Content = string.Format("Ready"));
   MessageBox.Show("Ok");
}

async Task SimulateParse(TaskQueue queue)
{
    Random rnd = new Random();
    int counter = 0; // representing some piece of data 
    for(int i = 0; i < 500; i++)
    {
        dispatch.Invoke(() => lblStatus2.Content = string.Format("Check {0}", ++counter));

        Thread.Sleep(25); //no await variant
        //await Task.Delay(25);
        
        // if some condition matched - queue work 
        if (rnd.Next(1, 11) < 2)
        {
            // Blocks thread even though Enqueue() has await inside
            queue.Enqueue(SimulateWork, counter); 

            //Task.Run(() => queue.Enqueue(SimulateWork, counter));
        }
    }   
}

async Task SimulateWork(object par)
{
    dispatch.Invoke(() => lblStatus.Content = string.Format("Working with {0}", par));

    Thread.Sleep(400); //no await variant
    //await Task.Delay(400);            
}

It seems, that it works only if launched task have await inside itself, i.e. if you trying to launch task without await inside it, it will block current thread.

This rig will work as intended, if commented lines are used, but it looks like excessive amount of calls, also, real versions of SimulateParse and SimulateWork does not need to await anything. Main question is - what is the optimal way to launch task with non-async function inside of it? Do i just need to encase them in a Task.Run() like in commented rows?

Upvotes: 0

Views: 499

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456322

TaskQueue is used here to run task one by one

It will run them one at a time, yes. SemaphoreSlim does have an implicit queue, but it's not strictly a FIFO-queue. Most synchronization primitives have a mostly-but-not-quite-FIFO implementation, which is Close Enough. This is because they are synchronization primitives, and not queues.

If you want an actual queue (i.e., with guaranteed FIFO order), then you should use a queue, such as TPL Dataflow or System.Threading.Channels.

if you trying to launch task without await inside it, it will block current thread.

All async methods begin executing on the current thread, as described on my blog. async does not mean "run on a different thread". If you want to run a method on a thread pool thread, then wrap that method call in Task.Run. That's a much cleaner solution than sprinkling Task.Delay throughout, and it's more efficient, too (no delays).

Upvotes: 2

Related Questions