0

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?

5
  • 1
    What does the dispatch.Invoke do? Is dispatch an event? is it a field containing the current dispatcher? Commented Apr 1, 2022 at 7:01
  • 2
    await queue.WaitAsync(); -- The linked class TaskQueue does not have a public WaitAsync method. Are you sure that all this complexity is required in order to demonstrate the blocking behavior of an async method without await? The compiler should give you a warning for doing that, with a quite explanatory message. Commented Apr 1, 2022 at 7:06
  • As an aside, I don't see what TaskQueue is adding here. Why not just await SimulateWork(counter)? However, it sounds like neither SimulateParse nor SimulateWork should be async at all - you should just be calling await Task.Run(() => SimulateParse()) in your handler to run your (synchronous) work on the thread pool. Commented Apr 1, 2022 at 9:17
  • 1
    Be clear - await doesn't "launch" anything. You somehow, anyhow, end up with a running Task and that's what you await. How you got that Task, how it completes, whether a thread is involved are all details that are irrelevant to the await statement. That you often create such a Task on the same line by e.g. calling a genuinely Async method is coincidence. Commented Apr 1, 2022 at 12:13
  • TaskQueue is used here to run task one by one, such as printer queue. Yes, SimulateParse and SimulateWork don't need to be async themselves, i mentioned that in last paragraph. Commented Apr 1, 2022 at 12:20

1 Answer 1

2

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).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.