Для цикла во многих потоках - PullRequest
2 голосов
/ 06 мая 2011

Как я могу выполнить каждый вызов цикла в другом потоке, но продолжение ExternalMethod должно ждать окончания последнего рабочего потока из цикла for (и синхронизации)?

ExternalMethod()
{
    //some calculations
    for (int i = 0; i < 10; i++)
    {
        SomeMethod(i);
    }
    //continuation ExternalMethod
}

Ответы [ 4 ]

5 голосов
/ 06 мая 2011

Один из подходов - использовать ManualResetEvent.

Рассмотрим следующий код (обратите внимание, что его не следует воспринимать как рабочий пример, он застрял на OSX, поэтому не нужно иметь VS или компилятор C # для проверки):

static ManualResetEvent mre = new ManualResetEvent(false);
static int DoneCount = 0;
static int DoneRequired = 9;
void ExternalMethod() {
        mre.Reset();
        for (int i = 0; i < 10; i++) {
                new Thread(new ThreadStart(ThreadVoid)).Start();
        }
        mre.WaitOne();
}

void ThreadVoid() {
        Interlocked.Increment(ref DoneCount);   

        if (DoneCount == DoneRequired) {
                mre.Set();
        }
}

ВАЖНО - Возможно, это не лучший способ сделать это, просто пример использования ManualResetEvent, и он отлично подойдет вашим потребностям.

Если вы используете .NET 4.0, вы можете использовать цикл Parallel.For - объясненный здесь .

4 голосов
/ 06 мая 2011
System.Threading.Tasks.Parallel.For(0, 10, (i) => SomeMethod(i));
1 голос
/ 06 мая 2011

Один из подходов заключается в использовании CountdownEvent.

ExternalMethod()
{
    //some calculations
    var finished = new CountdownEvent(1);
    for (int i = 0; i < 10; i++)
    {
        int capture = i; // This is needed to capture the loop variable correctly.
        finished.AddCount();
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              SomeMethod(capture);
            }
            finally
            {
              finished.Signal();
            }
          }, null);
    }
    finished.Signal();
    finished.Wait();
    //continuation ExternalMethod
}

Если CountdownEvent недоступен, то здесь есть альтернативный подход.

ExternalMethod()
{
    //some calculations
    var finished = new ManualResetEvent(false);
    int pending = 1;
    for (int i = 0; i < 10; i++)
    {
        int capture = i; // This is needed to capture the loop variable correctly.
        Interlocked.Increment(ref pending);
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              SomeMethod(capture);
            }
            finally
            {
              if (Interlocked.Decrement(ref pending) == 0) finished.Set();
            }
          }, null);
    }
    if (Interlocked.Decrement(ref pending) == 0) finished.Set();
    finished.WaitOne();
    //continuation ExternalMethod
}

Обратите внимание, что в обоих примерах сам цикл for обрабатывается как параллельный рабочий элемент (он находится в отдельном потоке от других рабочих элементов в конце концов), чтобы избежать действительно тонкого состояния гонки, которое может возникнуть, если первый рабочий элемент сигнализирует о событии до того, как следующий рабочий элемент будет поставлен в очередь.

0 голосов
/ 06 мая 2011

Для .NET 3.5 может быть что-то вроде этого:

Thread[] threads = new Thread[10];

for (int x = 0; x < 10; x++)
{
    threads[x] = new Thread(new ParameterizedThreadStart(ThreadFun));
    threads[x].Start(x);
}

foreach (Thread thread in threads) thread.Join();

Может показаться нелогичным использование Join() метода, но, поскольку вы эффективно выполняете шаблон типа WaitAll, не имеет значения, в каком порядке выполняются объединения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...