0

I'd like to understand the TPL a little bit better. How would I for example write something to the screen until a task finishes. I can't call 'await' because I want the main thread to be notified instead of proactively calling 'await' and I don't want to stop execution until the task is finished.

Some example code:

var task = Task.Run(()=>
{
   Task.Delay(10000);
});


while(true)
{
    Console.WriteLine("Running...");
    //I want to exit the loop the second 'task' finishes
    //and print 'finished'
}
Eitan
  • 1,434
  • 6
  • 21
  • 53
  • Register an event and pass it to your task and raise it when the job done and notify the caller. – Mojtaba Tajik Aug 30 '18 at 03:09
  • ... _want the main thread to be notified_ ... But for that you main thread needs to stop and wait for the notifiation. That is what `await` it. The other thing that you can do is periodically check (poll) whether that completed ir not. @KozhevnikovDmitry answer does that. – inquisitive Aug 30 '18 at 03:35
  • @inquisitive but I want it to run while it's awaiting. Polling will have some latency if the main thread isn't notified until calls await. – Eitan Aug 30 '18 at 03:39
  • 1
    Just check task.IsCompleted property inside the loop. – Dmytro Mukalov Aug 30 '18 at 05:00
  • look here https://stackoverflow.com/questions/50749250/how-can-i-know-if-a-task-is-completed – Barr J Aug 30 '18 at 06:16

4 Answers4

2

There are a a couple of ways you cam achieve this:

First you can use Lambda expression in Order to invoke your action, but look at the code:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Console.WriteLine("Application thread ID: {0}",
                        Thread.CurrentThread.ManagedThreadId);
      var t = Task.Run(() => {  Console.WriteLine("Task thread ID: {0}",
                                   Thread.CurrentThread.ManagedThreadId);
                             } );
      t.Wait();
   }
}
// The example displays the following output:
//       Application thread ID: 1
//  

notice the t.Wait():

The call to the Wait method ensures that the task completes and displays its output before the application ends. Otherwise, it is possible that the Main method will complete before the task finishes.

So we understand that it's imperative to call the Wait() method in order make sure that the task completes and displays its output.

You can use the second way also:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var list = new ConcurrentBag<string>();
      string[] dirNames = { ".", ".." };
      List<Task> tasks = new List<Task>();
      foreach (var dirName in dirNames) {
         Task t = Task.Run( () => { foreach(var path in Directory.GetFiles(dirName)) 
                                       list.Add(path); }  );
         tasks.Add(t);
      }
      Task.WaitAll(tasks.ToArray());
      foreach (Task t in tasks)
         Console.WriteLine("Task {0} Status: {1}", t.Id, t.Status);

      Console.WriteLine("Number of files read: {0}", list.Count);
   }
}

For more reference see Task.Run Method.


One highlight regarding your quesion:

taken from Asynchronous programming with async and await (C#):

An async method typically contains one or more occurrences of an await operator, but the absence of await expressions doesn’t cause a compiler error. If an async method doesn’t use an await operator to mark a suspension point, the method executes as a synchronous method does, despite the async modifier. The compiler issues a warning for such methods.

That implies that either way you will have to wait for your tasks to finish and the main thread will have to wait this way or another.

Barr J
  • 10,636
  • 1
  • 28
  • 46
  • In your examples it stops the main thread execution until the task finishes. I was wondering how to keep the main thread running until a task finishes. – Eitan Aug 30 '18 at 04:43
  • I wonder what you mean, if you keep the main thread running until the task finished you will have to wait for the task, otherwise the main thread might finish it's work before the task. There is no way to delay the main thread unless you explicitly tell the main thread to await for the task. – Barr J Aug 30 '18 at 05:05
  • I was thinking running a loop and then exiting the loop when a task finishes by checking some token but the problem is the latency between checking the token to see whether a task has finished. – Eitan Aug 30 '18 at 05:11
  • Look at the second piece of code, it might bring an idea in your mind – Barr J Aug 30 '18 at 05:15
  • also look here https://stackoverflow.com/questions/27764502/how-to-determine-whether-task-run-is-completed-within-a-loop – Barr J Aug 30 '18 at 05:17
  • So take for example the second piece of code. If any or all tasks take a long time, the Task.WaitAll will suspend the program and wait until all tasks are finished. I would like continue running the main thread until the tasks are finished, not when they are finished. – Eitan Aug 30 '18 at 05:28
  • There will be no easy way to achieve what you are seeking, you will have to wait. Tasks are async work, meaning that they are essentially threads. Meaning that the main thread won't actually stop, unless you implement lock mechanism. – Barr J Aug 30 '18 at 05:44
1

The ContinueWith function is a method available on the task that allows executing code after the task has finished execution. In simple words it allows continuation.

Things to note here is that ContinueWith also returns one Task. That means you can attach ContinueWith one task returned by this method.

   Task<string> t = Task.Run(() => LongRunningOperation("Continuewith", 500));  
   t.ContinueWith((t1) =>  
   {  
      Console.WriteLine("Running..."); 
   });  
Ankur Tripathi
  • 471
  • 2
  • 16
  • Eitan you want to run something in the background and after that work do you want to print "finished"? correct? and do to this work your main thread will be responsible? – Ankur Tripathi Aug 30 '18 at 03:46
  • I want to run something on the main thread continuously until a task finishes and stop the loop on thr writeain thread and write 'finished' or something but I'd like to do it the moment the thread finishes. – Eitan Aug 30 '18 at 04:28
1

You can achieve your goal using creating separate function to use in Task.Run and passing parameter by reference. Function should be something like below.

private void PerformTask(ref bool isComplete)
{
    System.Threading.Thread.Sleep(5000);
    isComplete = true;
}

Call above function from Task.Run. Your current function should be like below.

bool isComplete = false;
System.Threading.Tasks.Task.Run(() => PerformTask(ref isComplete));

while (!isComplete)
{
    Console.WriteLine("Running...");
    System.Threading.Thread.Sleep(1000);
}
Karan
  • 12,059
  • 3
  • 24
  • 40
  • I don't think this is thread-safe code. The main thread and the 'Task.Run thread' use the same variable isComplete. 'Task.Run thread' writes to that variable (the same memory address - ref bool isComplete) and main thread reads from it. You'd better to synchronize this. – Albert Lyubarsky Jul 08 '21 at 08:28
0

Try to do something like this:

var task = Task.Run(() =>
{
    Task.Delay(10000).Wait();
});


bool terminate = false;
while (!task.GetAwaiter().IsCompleted && !terminate)
{
    // do something 

    if (task.GetAwaiter().IsCompleted) break;
    // do something heavy

    if (task.GetAwaiter().IsCompleted) break;
    // do another heavy operation

    for (int i = 0; i < 10000; i++)
    {
        // it took too long!
        if (i == 1000)
        {
            terminate = true;
            break;
        }
    }

}
KozhevnikovDmitry
  • 1,660
  • 12
  • 27
  • 1
    That's a good solution but is there any way for the main thread to be notified so for example if the while loop takes a long time to run, it won't stop when the task finishes, it'll stop when it reaches the while condition – Eitan Aug 30 '18 at 03:41
  • When there are two running threads it takes time for notifications to go from one thread to other. The activity that your main thread is doing while the task runs - does it absolutely needs to be done on the main thread? Can you live with another task which does what main thread is doing now, and the main thread then just awaits the two tasks? – inquisitive Aug 30 '18 at 03:50
  • Use additional checking before heavy operations with while loop. You can also introduce termination flag and stop iteratiting by some logic. See updated example. – KozhevnikovDmitry Aug 30 '18 at 04:17
  • @inquisitive I don't really have a project or anything I was just curious if it was possible. The solution with the ,2 threads and awaiting both makes sense though. Thanks! – Eitan Aug 30 '18 at 04:35