C#: Waiting for All Threads to Complete

Waiting for all threads to complete, with a timeout

I still think using Join is simpler. Record the expected completion time (as Now+timeout), then, in a loop, do

if(!thread.Join(End-now))
throw new NotFinishedInTime();

C# Wait Till All Threads Complete Execution

Slightly improved by using a thread-safe collection (.NET 4.0 compatible):

List<Task> taskList = new List<Task>();
ConcurrentBag<object> allObjectAttributes = new ConcurrentBag<object>();

taskList.Add(Task.Factory.StartNew(() => allObjectAttributes.Add(GetObjectAttributes(TreeViewAttrs.Folder))));
taskList.Add(Task.Factory.StartNew(() => allObjectAttributes.Add(GetObjectAttributes(TreeViewAttrs.XMLFile))));
taskList.Add(Task.Factory.StartNew(() => allObjectAttributes.Add(GetObjectAttributes(TreeViewAttrs.TextFile))));
taskList.Add(Task.Factory.StartNew(() => allObjectAttributes.Add(GetObjectAttributes(TreeViewAttrs.Parent))));

Task.WaitAll(taskList.ToArray());

return allObjectAttributes;

Alternative approach: use Task.Result once all tasks have completed (thread-safe collection no longer required as only one thread modifies ArrayList):

Task<object>[] taskList = {
Task.Factory.StartNew(() => (object)GetObjectAttributes(TreeViewAttrs.Folder)),
Task.Factory.StartNew(() => (object)GetObjectAttributes(TreeViewAttrs.XMLFile)),
Task.Factory.StartNew(() => (object)GetObjectAttributes(TreeViewAttrs.TextFile)),
Task.Factory.StartNew(() => (object)GetObjectAttributes(TreeViewAttrs.Parent))
};

Task.WaitAll(taskList);

ArrayList allObjectAttributes = new ArrayList();

foreach (Task<object> task in taskList) {
allObjectAttributes.Add(task.Result);
}

return allObjectAttributes;

Significantly improved by using Task.WhenAll (.NET 4.5 only):

object[] allObjectAttributes = await Task.WhenAll(
Task.Run(() => GetObjectAttributes(TreeViewAttrs.Folder)),
Task.Run(() => GetObjectAttributes(TreeViewAttrs.XMLFile)),
Task.Run(() => GetObjectAttributes(TreeViewAttrs.TextFile)),
Task.Run(() => GetObjectAttributes(TreeViewAttrs.Parent))
);

return allObjectAttributes;

*Note: I used object as the generic parameter as you left the return type of GetObjectAttributes unspecified.

Waiting for all threads to end

Most of this code can be replaced with Ping.SendPingAsync :

static async Task Main(string[] args)
{

using var pingSender = new Ping();
var options = new PingOptions(64, true);
var reply=await pingSender.SendPingAsync(address,1200,options)
if(reply.Status == IPStatus.Success)
{
...
}
}

Concurrent Requests - One sender per request

SendPingAsync can't be called multiple times to send multiple requests from a single sender. Doing so throws an InvalidOperationException.

One solution is to create and use a separate sender per call :

async Task<Reply> PingAsync(IPAddress address)
{
using var pingSender = new Ping();
var options = new PingOptions(64, true);
var reply=await pingSender.SendPingAsync(address,1200,options)
}

Combined with LINQ, it's possible to start multiple pings concurrently and collect the results:

var addresses=new List<IPAddress>();
...
//Create the tasks with LINQ
var tasks=addresses.Select(address=>PingAsync(address))
.ToArray();
//And wait for all of them to complete
var replies=await Task.WhenAll(tasks);

//Get the successful pings:
var successes=replies.Where(reply=>reply.Status==IPStatus.Success)
.Select(reply=>reply.Address.ToString())
.ToArray();

The first LINQ query will call PingAsync for every address and returns the Task<Reply> object returned by each call:

var tasks=addresses.Select(address=>PingAsync(address))
.ToArray();

The query is actually evaluated when ToArray() is called. All ping requests will start at the same time.

The next call awaits all of them to complete :

var replies=await Task.WhenAll(tasks);

And finally, the last LINQ query retrieves the IPs of the successful pings:

var successes=replies.Where(reply=>reply.Status==IPStatus.Success)
.Select(reply=>reply.Address.ToString())
.ToArray();

Create multiple threads and wait for all of them to complete

It depends which version of the .NET Framework you are using. .NET 4.0 made thread management a whole lot easier using Tasks:

class Program
{
static void Main(string[] args)
{
Task task1 = Task.Factory.StartNew(() => doStuff());
Task task2 = Task.Factory.StartNew(() => doStuff());
Task task3 = Task.Factory.StartNew(() => doStuff());

Task.WaitAll(task1, task2, task3);
Console.WriteLine("All threads complete");
}

static void doStuff()
{
//do stuff here
}
}

In previous versions of .NET you could use the BackgroundWorker object, use ThreadPool.QueueUserWorkItem(), or create your threads manually and use Thread.Join() to wait for them to complete:

static void Main(string[] args)
{
Thread t1 = new Thread(doStuff);
t1.Start();

Thread t2 = new Thread(doStuff);
t2.Start();

Thread t3 = new Thread(doStuff);
t3.Start();

t1.Join();
t2.Join();
t3.Join();

Console.WriteLine("All threads complete");
}

C# .Net Wait for all threads to complete and get return values

Try using a Task<string> instead of a Task and querying the Task<string>.Result

Waiting for threads to complete

DISCLAIMER: I posted this answer only because it does what the OP asked for and I didn't take the answer from Servy: there's nothing that you can do. But I advice to NOT use this in a production environment. Personally I would ditch that library and implement it myself.

This said, as a test I created a class library that holds a method DoWork which starts a background thread that runs for 10 seconds:

public class ThirdParty
{
public static void DoWork()
{
new Thread(() => Thread.Sleep(10000)) { IsBackground = true }.Start();
}
}

Now, the idea is to compare the running threads before and after the ThirdParty.DoWork call. After the call, one (or more) thread-id's will be collected and translated into thread handle(s) by using OpenThread. At the end, you can call WaitForMultipleObjects to wait until the third party background thread has finished doing its work.

class Program
{
[DllImport("kernel32.dll")]
static extern uint WaitForMultipleObjects(int nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds);

[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, int dwThreadId);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

const uint SYNCHRONIZE = 0x00100000;

static IntPtr GetThreadHandleById(int threadId)
{
return OpenThread(SYNCHRONIZE, false, threadId);
}

static void Main(string[] args)
{
var threads = new List<int>();
foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
threads.Add(thread.Id);

ThirdParty.DoWork();

var threadHandlesToWaitFor = new List<IntPtr>();
foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
if (!threads.Contains(thread.Id))
threadHandlesToWaitFor.Add(GetThreadHandleById(thread.Id));

Console.WriteLine("Waiting for {0} thread(s) to finish...", threadHandlesToWaitFor.Count);
WaitForMultipleObjects(threadHandlesToWaitFor.Count, threadHandlesToWaitFor.ToArray(), true, 0xffffffff);

foreach (var handle in threadHandlesToWaitFor)
CloseHandle(handle);
}
}

In essence, we're waiting for an unmanaged thread that seems to run the spawned managed thread. This can break in the future because .NET does not guarantee that every managed thread runs on its own unmanaged thread.

C# wait for all threads to finish in Main()

Perhaps Thread.Join() is what you're looking for?

you also might find this article usefull.

Wait for threads to complete

I suggest to use TPL to get this done. You won't need to spawn so many threads. By default TPL will use thread pool for that:

using System;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{
private const int TaskCount = 15;

static void Main(string[] args)
{
var tasks = new Task[TaskCount];
for (var index = 0; index < TaskCount; index++)
{
tasks[index] = Task.Factory.StartNew(Method);
}
Task.WaitAll(tasks);

Console.WriteLine("Some text");
}

private static void Method()
{
for (int i = 0; i < 15; i++)
{
Console.WriteLine(i);
}
}
}
}


Related Topics



Leave a reply



Submit