views:

57

answers:

2

I work with new Parallel.For that creates multiple threads to perform the same operation.

In case one of the threads fail, it means that I'm working "too fast" and I need to put all the threads to rest for a few seconds.

Is there a way to perform something like Thread.Sleep - only to do the same on all threads at once?

+2  A: 

This is a direct answer to the question, except for the Parallel.For bit. It really is a horrible pattern; you should probably be using a proper synchronization mechanism, and get the worker threads to, without preemption, occasionally check if they need to 'back off.' In addition, this uses Thread.Suspend and Thread.Resume which are both deprecated, and with good reason (from Thread.Suspend):

"Do not use the Suspend and Resume methods to synchronize the activities of threads. You have no way of knowing what code a thread is executing when you suspend it. If you suspend a thread while it holds locks during a security permission evaluation, other threads in the AppDomain might be blocked. If you suspend a thread while it is executing a class constructor, other threads in the AppDomain that attempt to use that class are blocked. Deadlocks can occur very easily."

(Untested)

public class Worker
{
    private readonly Thread[] _threads;
    private readonly object _locker = new object();
    private readonly TimeSpan _tooFastSuspensionSpan;
    private DateTime _lastSuspensionTime;

    public Worker(int numThreads, TimeSpan tooFastSuspensionSpan)
    {
        _tooFastSuspensionSpan = tooFastSuspensionSpan;
        _threads = Enumerable.Repeat(new ThreadStart(DoWork), numThreads)
                             .Select(ts => new Thread(ts))
                             .ToArray();
    }

    public void Run()
    {
        foreach (var thread in _threads)
        {
            thread.Start();
        }
    }

    private void DoWork()
    {
        while (!IsWorkComplete())
        {
            try
            {
                // Do work here

            }
            catch (TooFastException)
            {
                SuspendAll();
            }
        }

    }

    private void SuspendAll()
    {
        lock (_locker)
        {
            // We don't want N near-simultaneous failures causing a sleep-duration of N * _tooFastSuspensionSpan
            // 1 second is arbitrary. We can't be deterministic about it since we are forcefully suspending threads
            var now = DateTime.Now;
            if (now.Subtract(_lastSuspensionTime) < _tooFastSuspensionSpan + TimeSpan.FromSeconds(1))
                return;

            _lastSuspensionTime = now;

            var otherThreads = _threads.Where(t => t.ManagedThreadId != Thread.CurrentThread.ManagedThreadId).ToArray();

            foreach (var otherThread in otherThreads)
                otherThread.Suspend();

            Thread.Sleep(_tooFastSuspensionSpan);

            foreach (var otherThread in otherThreads)
                otherThread.Resume();
        }
    }

}
Ani
I understand your solution. It makes sense. Thank you.
Faruz
A: 

You need to create an inventory of your worker threads and then perhaps you can use Thread.Suspend and Resume methods. Mind you that using Suspend can be dangerous (for example, thread may have acquired lock before suspending). And suspend/resume have been marked obsolate due to such issues.

VinayC