How to call a GPU task periodically using Hybridizer - c#

I'm trying to use hybridizer for computations using a GPU in C#. I tried to embed my GPU code in a .NET library use Altimesh Hybridizer. Let's call it GPU_DLL.
This library has to be used in another project, let's call it Test_GPU_Caller. The GPU code is called periodically using a timer as shown in the following code :
class Program
{
static GPU_DLL.GPU_DLL gpuDllTest = new GPU_DLL.GPU_DLL();
static void Main(string[] args)
{
Timer tmr = new Timer(100);
tmr.Elapsed += Tmr_Elapsed;
tmr.Start();
while (true) ;
}
private static void Tmr_Elapsed(object sender, ElapsedEventArgs e)
{
gpuDllTest.Test("GPU_DLL_CUDA.dll");
}
}
Test function is implemented in a .NET library with the following code :
public class GPU_DLL
{
[EntryPoint("run")]
public void Run(int N, double[] a, double[] b)
{
Parallel.For(0, N, i => { a[i] += b[i]; });
}
public void Test(string dllName)
{
// 268 MB allocated on device -- should fit in every CUDA compatible GPU
int N = 1024 * 1024 * 16;
double[] acuda = new double[N];
double[] adotnet = new double[N];
double[] b = new double[N];
Random rand = new Random();
//Initialize acuda et adotnet and b by some doubles randoms, acuda and adotnet have same numbers.
for (int i = 0; i < N; ++i)
{
acuda[i] = rand.NextDouble();
adotnet[i] = acuda[i];
b[i] = rand.NextDouble();
}
cudaDeviceProp prop;
cuda.GetDeviceProperties(out prop, 0);
HybRunner runner = HybRunner.Cuda(dllName).SetDistrib(prop.multiProcessorCount * 16, 128);
// create a wrapper object to call GPU methods instead of C#
dynamic wrapped = runner.Wrap(this);
// run the method on GPU
wrapped.Run(N, acuda, b);
// run .Net method
Run(N, adotnet, b);
// verify the results
for (int k = 0; k < N; ++k)
{
if (acuda[k] != adotnet[k])
Console.Out.WriteLine("ERROR !");
}
Console.Out.WriteLine("DONE");
//Thread.Sleep(10000);
}
}
GPU_DLL has been Hybridized first, and compiled. When i launch the program, i get an error :
System.ArgumentException : 'Un élément avec la même clé a déjà été ajouté.' (in french...)
Does someone know how to use GPU code called periodically using Hybridizer ? A sample project is welcome ;)
Best regards,
Valentin

The problem you are facing seems to relate to timer calling the function a second time before first call has finished. According to documentation, your timer setup will call the timer function callback every 100 milliseconds, however it will not wait for previous run to be done (running it on my system requires 1.3 seconds for first iteration, and ~650ms on subsequent calls).
HybRunner is not thread-safe. Also, note that the CUDA context is attached to a thread, and using another thread requires another CUDA context.
Here is a alternate implementation to address your issue using a worker thread that executes GPU-related tasks:
First, declare synchronization components:
static bool workerrunning = false;
static object started = new object();
static Queue<object> tasks = new Queue<object>();
static object watch = new object();
The worker thread loop could be the following:
public static void HybridLoop(object p)
{
lock (started)
{
workerrunning = true;
Monitor.Pulse(started);
}
while (workerrunning)
{
object currenttask = null;
// get next task
lock (watch)
{
if (tasks.Count == 0)
{
Monitor.Wait(watch);
}
if (tasks.Count != 0)
{
currenttask = tasks.Dequeue();
// NOTE: here, we illustrate more tasks than doable => clear
tasks.Clear();
}
}
if (currenttask != null)
{
gpuDllTest.Test("GPU_DLL_CUDA.dll");
}
}
}
Here, we clear the task queue as it is populated faster than consumed.
The code to start the worker thread:
System.Threading.Thread hybridrunner = new System.Threading.Thread(HybridLoop);
lock(started)
{
hybridrunner.Start();
Monitor.Wait(started);
}
Once worker is started, timer can be started:
System.Timers.Timer tmr = new System.Timers.Timer(100);
tmr.Elapsed += Tmr_Elapsed;
tmr.Start();
Console.Out.WriteLine("Runner started - press return to stop");
var key = Console.Read();
Your timer elapse function is simply a task enqueing:
private static void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock (watch)
{
tasks.Enqueue(1);
Monitor.Pulse(watch);
}
}
Once done, runner can be stopped to avoid tail-running thread:
workerrunning = false;
lock (watch)
{
tmr.Stop();
tasks.Enqueue(0);
Monitor.Pulse(watch);
}
Console.Out.WriteLine("Runner stopping");
hybridrunner.Join();
Console.Out.WriteLine("Runner stopped");
Example execution log:
DONE
Execution took 1026 milliseconds
DONE
Execution took 642 milliseconds
DONE
Execution took 614 milliseconds
Runner started - press return to stop
DONE
DONE
DONE
Runner stopping
DONE
Runner stopped
As a final note, you may want to do some initialization such as HybRunner.Cuda(), only once, and then perform other tasks in the same worker thread.

Related

Why is assigning a variable with a class, which constructs a thread working faster than aborting the thread instead?

I would like to run a thread and abort it when I need to run it again while the thread can be still alive, but I noticed that aborting is slow due to how aborting a thread works.
Therefore I have the following implementation of simulations, which construct threads and measure the time for the simulation:
private Thread _t;
public void Main()
{
SimulateAbortThread();
SimulateThreadClass();
}
private void SimulateAbortThread()
{
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < 100; i++)
{
RunThread();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
private void RunThread()
{
if (_t != null && _t.IsAlive) _t.Abort();
_t = new Thread(() =>
{
//doStuff();
});
_t.Start();
}
private void SimulateThreadClass()
{
var thread = new ThreadClass();
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < 100; i++)
{
thread = new ThreadClass();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
private class ThreadClass
{
public ThreadClass()
{
new Thread(() =>
{
//doStuff();
}).Start();
}
}
I discovered that creating a new instance of a class, which constructs a new thread, is faster than aborting the thread itself. The results of both simulations in real cases are identical but, due to how aborting works the simulations only differ in speed, which is why I am currently using the second simulation.
I would like to know, why the second simulation is running faster and how I can avoid assigning a variable a new instance of a class to "restart" threads while they still might run?

Repeat a task (TPL) in windows service, using ContinueWith

I have a windows service (written in C#) that use the task parallel library dll to perform some parallel tasks (5 tasks a time)
After the tasks are executed once I would like to repeat the same tasks on an on going basis (hourly). Call the QueuePeek method
Do I use a timer or a counter like I have setup in the code snippet below?
I am using a counter to set up the tasks, once I reach five I exit the loop, but I also use a .ContinueWith to decrement the counter, so my thought is that the counter value would be below 5 hence the loop would continue. But my ContinueWith seems to be executing on the main thread and the loop then exits.
The call to DecrementCounter using the ContinueWith does not seem to work
FYI : The Importer class is to load some libraries using MEF and do the work
This is my code sample:
private void QueuePeek()
{
var list = SetUpJobs();
while (taskCounter < 5)
{
int j = taskCounter;
Task task = null;
task = new Task(() =>
{
DoLoad(j);
});
taskCounter += 1;
tasks[j] = task;
task.ContinueWith((t) => DecrementTaskCounter());
task.Start();
ds.SetJobStatus(1);
}
if (taskCounter == 0)
Console.WriteLine("Completed all tasks.");
}
private void DoLoad(int i)
{
ILoader loader;
DataService.DataService ds = new DataService.DataService();
Dictionary<int, dynamic> results = ds.AssignRequest(i);
var data = results.Where(x => x.Key == 2).First();
int loaderId = (int)data.Value;
Importer imp = new Importer();
loader = imp.Run(GetLoaderType(loaderId));
LoaderProcessor lp = new LoaderProcessor(loader);
lp.ExecuteLoader();
}
private void DecrementTaskCounter()
{
Console.WriteLine(string.Format("Decrementing task counter with threadId: {0}",Thread.CurrentThread.ManagedThreadId) );
taskCounter--;
}
I see a few issues with your code that can potentially lead to some hard to track-down bugs. First, if using a counter that all of the tasks can potentially be reading and writing to at the same time, try using Interlocked. For example:
Interlocked.Increment(ref _taskCounter); // or Interlocked.Decrement(ref _taskCounter);
If I understand what you're trying to accomplish, I think what you want to do is to use a timer that you re-schedule after each group of tasks is finished.
public class Worker
{
private System.Threading.Timer _timer;
private int _timeUntilNextCall = 3600000;
public void Start()
{
_timer = new Timer(new TimerCallback(QueuePeek), null, 0, Timeout.Infinite);
}
private void QueuePeek(object state)
{
int numberOfTasks = 5;
Task[] tasks = new Task[numberOfTasks];
for(int i = 0; i < numberOfTasks; i++)
{
tasks[i] = new Task(() =>
{
DoLoad();
});
tasks[i].Start();
}
// When all tasks are complete, set to run this method again in x milliseconds
Task.Factory.ContinueWhenAll(tasks, (t) => { _timer.Change(_timeUntilNextCall, Timeout.Infinite); });
}
private void DoLoad() { }
}

How to implement Barrier class from .NET 4 functionality in .NET 3.5

For some reasons I have to stick to .NET 3.5 and I need a functionality of Barrier class from .NET 4. I have a bunch of threads that do some work and I want them to wait for each other until all are done. When all are done I want that they do the job again and again in the similar manner.
Encouraged by the thread Difference between Barrier in C# 4.0 and WaitHandle in C# 3.0? I have decided to implement the Barrier functionality with AutoResetEvent and WaitHandle classes.
Altough I encounter a problem with my code:
class Program
{
const int numOfThreads = 3;
static AutoResetEvent[] barrier = new AutoResetEvent[numOfThreads];
static Random random = new Random(System.DateTime.Now.Millisecond);
static void barriers2(object barrierObj)
{
AutoResetEvent[] barrierLocal = (AutoResetEvent[])barrierObj;
string name = Thread.CurrentThread.Name;
for (int i = 0; i < 10; i++)
{
int sleepTime = random.Next(2000, 10000);
System.Console.Out.WriteLine("Thread {0} at the 'barrier' will sleep for {1}.", name, sleepTime);
Thread.Sleep(sleepTime);
System.Console.Out.WriteLine("Thread {0} at the 'barrier' with time {1}.", name, sleepTime);
int currentId = Convert.ToInt32(name);
//for(int z = 0; z < numOfThreads; z++)
barrierLocal[currentId].Set();
WaitHandle.WaitAll(barrier);
/*
for (int k = 0; k < numOfThreads; k++)
{
if (k == currentId)
{
continue;
}
System.Console.Out.WriteLine("Thread {0} is about to wait for the singla from thread: {1}", name, k);
barrierLocal[k].WaitOne();
System.Console.Out.WriteLine("Thread {0} is about to wait for the singla from thread: {1}. done", name, k);
}
*/
}
}
static void Main(string[] args)
{
for (int i = 0; i < numOfThreads; i++)
{
barrier[i] = new AutoResetEvent(false);
}
for (int i = 0; i < numOfThreads; i++)
{
Thread t = new Thread(Program.barriers2);
t.Name = Convert.ToString(i);
t.Start(barrier);
}
}
}
The output I receive is as follows:
Thread 0 at the 'barrier' will sleep for 7564
Thread 1 at the 'barrier' will sleep for 5123
Thread 2 at the 'barrier' will sleep for 4237
Thread 2 at the 'barrier' with time 4237
Thread 1 at the 'barrier' with time 5123
Thread 0 at the 'barrier' with time 7564
Thread 0 at the 'barrier' will sleep for 8641
Thread 0 at the 'barrier' with time 8641
And that's it. After the last line there is no more output and the app does not terminate. It looks like there is some sort of deadlock. However can not find the issue. Any help welcome.
Thanks!
That's because you use AutoResetEvent. One of the thread's WaitAll() call is going to complete first. Which automatically causes Reset() on all the AREs. Which prevents the other threads from ever completing their WaitAll() calls.
A ManualResetEvent is required here.
Download the Reactive Extensions backport for .NET 3.5. You will find the Barrier class along with the other useful concurrent data structures and synchronization mechanisms that were released in .NET 4.0.
Here is my implementation I use for my XNA game. Barrier was not available when I wrote this, and I am still stuck with .Net 3.5. It requires three sets of ManualResetEvents, and a counter array to keep phase.
using System;
using System.Threading;
namespace Colin.Threading
{
/// <summary>
/// Threading primitive for "barrier" sync, where N threads must stop at certain points
/// and wait for all their bretheren before continuing.
/// </summary>
public sealed class NThreadGate
{
public int mNumThreads;
private ManualResetEvent[] mEventsA;
private ManualResetEvent[] mEventsB;
private ManualResetEvent[] mEventsC;
private ManualResetEvent[] mEventsBootStrap;
private Object mLockObject;
private int[] mCounter;
private int mCurrentThreadIndex = 0;
public NThreadGate(int numThreads)
{
this.mNumThreads = numThreads;
this.mEventsA = new ManualResetEvent[this.mNumThreads];
this.mEventsB = new ManualResetEvent[this.mNumThreads];
this.mEventsC = new ManualResetEvent[this.mNumThreads];
this.mEventsBootStrap = new ManualResetEvent[this.mNumThreads];
this.mCounter = new int[this.mNumThreads];
this.mLockObject = new Object();
for (int i = 0; i < this.mNumThreads; i++)
{
this.mEventsA[i] = new ManualResetEvent(false);
this.mEventsB[i] = new ManualResetEvent(false);
this.mEventsC[i] = new ManualResetEvent(false);
this.mEventsBootStrap[i] = new ManualResetEvent(false);
this.mCounter[i] = 0;
}
}
/// <summary>
/// Adds a new thread to the gate system.
/// </summary>
/// <returns>Returns a thread ID for this thread, to be used later when waiting.</returns>
public int AddThread()
{
lock (this.mLockObject)
{
this.mEventsBootStrap[this.mCurrentThreadIndex].Set();
this.mCurrentThreadIndex++;
return this.mCurrentThreadIndex - 1;
}
}
/// <summary>
/// Stop here and wait for all the other threads in the NThreadGate. When all the threads have arrived at this call, they
/// will unblock and continue.
/// </summary>
/// <param name="myThreadID">The thread ID of the caller</param>
public void WaitForOtherThreads(int myThreadID)
{
// Make sure all the threads are ready.
WaitHandle.WaitAll(this.mEventsBootStrap);
// Rotate between three phases.
int phase = this.mCounter[myThreadID];
if (phase == 0) // Flip
{
this.mEventsA[myThreadID].Set();
WaitHandle.WaitAll(this.mEventsA);
this.mEventsC[myThreadID].Reset();
}
else if (phase == 1) // Flop
{
this.mEventsB[myThreadID].Set();
WaitHandle.WaitAll(this.mEventsB);
this.mEventsA[myThreadID].Reset();
}
else // Floop
{
this.mEventsC[myThreadID].Set();
WaitHandle.WaitAll(this.mEventsC);
this.mEventsB[myThreadID].Reset();
this.mCounter[myThreadID] = 0;
return;
}
this.mCounter[myThreadID]++;
}
}
}
Setting up the thread gate:
private void SetupThreads()
{
// Make an NThreadGate for N threads.
this.mMyThreadGate = new NThreadGate(Environment.ProcessorCount);
// Make some threads...
// e.g. new Thread(new ThreadStart(this.DoWork);
}
Thread worker method:
private void DoWork()
{
int localThreadID = this.mMyThreadGate.AddThread();
while (this.WeAreStillRunning)
{
// Signal this thread as waiting at the barrier
this.mMyThreadGate.WaitForOtherThreads(localThreadID);
// Synchronized work here...
// Signal this thread as waiting at the barrier
this.mMyThreadGate.WaitForOtherThreads(localThreadID);
// Synchronized work here...
// Signal this thread as waiting at the barrier
this.mMyThreadGate.WaitForOtherThreads(localThreadID);
}
}

ThreadQueue problems in "Accelerated C# 2008"

Example for threading queue book "Accelerated C# 2008" (CrudeThreadPool class) not work correctly. If I insert long job in WorkFunction() on 2-processor machine executing for next task don't run before first is over. How to solve this problem? I want to load the processor to 100 percent
public class CrudeThreadPool
{
static readonly int MAX_WORK_THREADS = 4;
static readonly int WAIT_TIMEOUT = 2000;
public delegate void WorkDelegate();
public CrudeThreadPool()
{
stop = 0;
workLock = new Object();
workQueue = new Queue();
threads = new Thread[MAX_WORK_THREADS];
for (int i = 0; i < MAX_WORK_THREADS; ++i)
{
threads[i] = new Thread(new ThreadStart(this.ThreadFunc));
threads[i].Start();
}
}
private void ThreadFunc()
{
lock (workLock)
{
int shouldStop = 0;
do
{
shouldStop = Interlocked.Exchange(ref stop, stop);
if (shouldStop == 0)
{
WorkDelegate workItem = null;
if (Monitor.Wait(workLock, WAIT_TIMEOUT))
{
// Process the item on the front of the queue
lock (workQueue)
{
workItem = (WorkDelegate)workQueue.Dequeue();
}
workItem();
}
}
} while (shouldStop == 0);
}
}
public void SubmitWorkItem(WorkDelegate item)
{
lock (workLock)
{
lock (workQueue)
{
workQueue.Enqueue(item);
}
Monitor.Pulse(workLock);
}
}
public void Shutdown()
{
Interlocked.Exchange(ref stop, 1);
}
private Queue workQueue;
private Object workLock;
private Thread[] threads;
private int stop;
}
public class EntryPoint
{
static void WorkFunction()
{
Console.WriteLine("WorkFunction() called on Thread 0}", Thread.CurrentThread.GetHashCode());
//some long job
double s = 0;
for (int i = 0; i < 100000000; i++)
s += Math.Sin(i);
}
static void Main()
{
CrudeThreadPool pool = new CrudeThreadPool();
for (int i = 0; i < 10; ++i)
{
pool.SubmitWorkItem(
new CrudeThreadPool.WorkDelegate(EntryPoint.WorkFunction));
}
pool.Shutdown();
}
}
I can see 2 problems:
Inside ThreadFunc() you take a lock(workLock) for the duration of the method, meaning your threadpool is no longer async.
in the Main() method, you close down the threadpool w/o waiting for it to finish. Oddly enough that is why it is working now, stopping each ThreadFunc after 1 loop.
It's hard to tell because there's no indentation, but it looks to me like it's executing the work item while still holding workLock - which is basically going to serialize all the work.
If at all possible, I suggest you start using the Parallel Extensions framework in .NET 4, which has obviously had rather more time spent on it. Otherwise, there's the existing thread pool in the framework, and there are other implementations around if you're willing to have a look. I have one in MiscUtil although I haven't looked at the code for quite a while - it's pretty primitive.

Waiting for all threads to complete, with a timeout

I'm running into a common pattern in the code that I'm writing, where I need to wait for all threads in a group to complete, with a timeout. The timeout is supposed to be the time required for all threads to complete, so simply doing Thread.Join(timeout) for each thread won't work, since the possible timeout is then timeout * numThreads.
Right now I do something like the following:
var threadFinishEvents = new List<EventWaitHandle>();
foreach (DataObject data in dataList)
{
// Create local variables for the thread delegate
var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset);
threadFinishEvents.Add(threadFinish);
var localData = (DataObject) data.Clone();
var thread = new Thread(
delegate()
{
DoThreadStuff(localData);
threadFinish.Set();
}
);
thread.Start();
}
Mutex.WaitAll(threadFinishEvents.ToArray(), timeout);
However, it seems like there should be a simpler idiom for this sort of thing.
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();
With .NET 4.0 I find System.Threading.Tasks a lot easier to work with. Here's spin-wait loop which works reliably for me. It blocks the main thread until all the tasks complete. There's also Task.WaitAll, but that hasn't always worked for me.
for (int i = 0; i < N; i++)
{
tasks[i] = Task.Factory.StartNew(() =>
{
DoThreadStuff(localData);
});
}
while (tasks.Any(t => !t.IsCompleted)) { } //spin wait
This doesn't answer the question (no timeout), but I've made a very simple extension method to wait all threads of a collection:
using System.Collections.Generic;
using System.Threading;
namespace Extensions
{
public static class ThreadExtension
{
public static void WaitAll(this IEnumerable<Thread> threads)
{
if(threads!=null)
{
foreach(Thread thread in threads)
{ thread.Join(); }
}
}
}
}
Then you simply call:
List<Thread> threads=new List<Thread>();
//Add your threads to this collection
threads.WaitAll();
Since the question got bumped I will go ahead and post my solution.
using (var finished = new CountdownEvent(1))
{
for (DataObject data in dataList)
{
finished.AddCount();
var localData = (DataObject)data.Clone();
var thread = new Thread(
delegate()
{
try
{
DoThreadStuff(localData);
threadFinish.Set();
}
finally
{
finished.Signal();
}
}
);
thread.Start();
}
finished.Signal();
finished.Wait(YOUR_TIMEOUT);
}
Off the top of my head, why don't you just Thread.Join(timeout) and remove the time it took to join from the total timeout?
// pseudo-c#:
TimeSpan timeout = timeoutPerThread * threads.Count();
foreach (Thread thread in threads)
{
DateTime start = DateTime.Now;
if (!thread.Join(timeout))
throw new TimeoutException();
timeout -= (DateTime.Now - start);
}
Edit: code is now less pseudo. don't understand why you would mod an answer -2 when the answer you modded +4 is exactly the same, only less detailed.
This may not be an option for you, but if you can use the Parallel Extension for .NET then you could use Tasks instead of raw threads and then use Task.WaitAll() to wait for them to complete.
I read the book C# 4.0: The Complete Reference of Herbert Schildt. The author use join to give a solution :
class MyThread
{
public int Count;
public Thread Thrd;
public MyThread(string name)
{
Count = 0;
Thrd = new Thread(this.Run);
Thrd.Name = name;
Thrd.Start();
}
// Entry point of thread.
void Run()
{
Console.WriteLine(Thrd.Name + " starting.");
do
{
Thread.Sleep(500);
Console.WriteLine("In " + Thrd.Name +
", Count is " + Count);
Count++;
} while (Count < 10);
Console.WriteLine(Thrd.Name + " terminating.");
}
}
// Use Join() to wait for threads to end.
class JoinThreads
{
static void Main()
{
Console.WriteLine("Main thread starting.");
// Construct three threads.
MyThread mt1 = new MyThread("Child #1");
MyThread mt2 = new MyThread("Child #2");
MyThread mt3 = new MyThread("Child #3");
mt1.Thrd.Join();
Console.WriteLine("Child #1 joined.");
mt2.Thrd.Join();
Console.WriteLine("Child #2 joined.");
mt3.Thrd.Join();
Console.WriteLine("Child #3 joined.");
Console.WriteLine("Main thread ending.");
Console.ReadKey();
}
}
I was tying to figure out how to do this but i could not get any answers from google.
I know this is an old thread but here was my solution:
Use the following class:
class ThreadWaiter
{
private int _numThreads = 0;
private int _spinTime;
public ThreadWaiter(int SpinTime)
{
this._spinTime = SpinTime;
}
public void AddThreads(int numThreads)
{
_numThreads += numThreads;
}
public void RemoveThread()
{
if (_numThreads > 0)
{
_numThreads--;
}
}
public void Wait()
{
while (_numThreads != 0)
{
System.Threading.Thread.Sleep(_spinTime);
}
}
}
Call Addthreads(int numThreads) before executing a thread(s).
Call RemoveThread() after each one has completed.
Use Wait() at the point that you want to wait for all the threads to complete
before continuing
Possible solution:
var tasks = dataList
.Select(data => Task.Factory.StartNew(arg => DoThreadStuff(data), TaskContinuationOptions.LongRunning | TaskContinuationOptions.PreferFairness))
.ToArray();
var timeout = TimeSpan.FromMinutes(1);
Task.WaitAll(tasks, timeout);
Assuming dataList is the list of items and each item needs to be processed in a separate thread.
Here is an implementation inspired by Martin v. Löwis's answer:
/// <summary>
/// Blocks the calling thread until all threads terminate, or the specified
/// time elapses. Returns true if all threads terminated in time, or false if
/// at least one thread has not terminated after the specified amount of time
/// elapsed.
/// </summary>
public static bool JoinAll(IEnumerable<Thread> threads, TimeSpan timeout)
{
ArgumentNullException.ThrowIfNull(threads);
if (timeout < TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(timeout));
Stopwatch stopwatch = Stopwatch.StartNew();
foreach (Thread thread in threads)
{
if (!thread.IsAlive) continue;
TimeSpan remaining = timeout - stopwatch.Elapsed;
if (remaining < TimeSpan.Zero) return false;
if (!thread.Join(remaining)) return false;
}
return true;
}
For measuring the remaining time, instead of the DateTime.Now it uses a Stopwatch. The Stopwatch component is not sensitive to system-wide clock adjustments.
Usage example:
bool allTerminated = JoinAll(new[] { thread1, thread2 }, TimeSpan.FromSeconds(10));
The timeout must be a positive or zero TimeSpan. The Timeout.InfiniteTimeSpan constant is not supported.

Categories

Resources