2009-03-16 3 views
6

Я столкнулся с довольно неудобным затруднительным положением в проекте на работе. Нам необходимо создать пользователей через 4 или 5 различных сервисов и настроить его таким образом, чтобы в случае сбоя все они терпели неудачу. Они заключены в блок транзакций.Синхронизация асинхронного вызова в C#

Одна из услуг, которые нам необходимы, чтобы добавить пользователей к необходимости telnetting и подтасовки некоторых данных. Есть и другие способы сделать это (это стоит денег), но пока это то, за чем мы застряли. Добавление одного пользователя занимает примерно 3 минуты. Мы собираемся поработать над тем, чтобы получить это значительно, как можно себе представить, но это не совсем так. Этот вызов является асинхронным, и он должен работать правильно. Пунктирная линия: между сервисом может быть не более 10 подключений.

Наш проект настроен для создания пользователей в партии. Так потенциально 50 пользователей создавались за раз. Это создает проблему, когда через telnet можно сделать только 10 подключений, и обработанный пользователь вряд ли займет очень много времени, кроме службы telnet. Теперь мне нужно синхронизировать этот процесс, чтобы остальное не могло продолжаться, пока оно не закончится.

Мы используем callbacks и делегаты с асинхронными вызовами для реализации функций. Каков был бы лучший способ инкапсулировать асинхронную часть, а не продолжать, пока она не будет завершена?

Должен ли мы настроить цикл, который завершается только после завершения вызова? Есть ли что-то в библиотеке Threading, которая могла бы помочь? Я никогда не работал с потоками раньше, так что это будет для меня первым. Какие инструменты помогут решить эту проблему?

EDIT:

Если я использую BeginInvoke/EndInvoke шаблон, будет асинхронных звонки в пределах первого делегата честь начала/конца также?

Пример:

public void dele1(string message) { 
    Console.Write(message); 
    delegate2 del2 = new delegate2; 
    del2(); 
    Console.Write("End of Delegate 2"); 
} 

public void dele2() { 
    // Long Processing 
    Console.Write("Delegate 2"); 
} 

public delegate void delegate1(String message); 
public delegate void delegate2(); 

delegate1 del1 = new delegate1(dele1); 
del1("Delegate 1").BeginInvoke; 
del1().EndInvoke; 
Console.Write("End of Delegate 1"); 

// Ожидаемый результат (End Invoke ждет, пока делегат 2 не будет завершен):

Delegate 1 
End of Delegate 2 
Delegate 2 
End of Delegate 1 

// Или (End Invoke только ждет делегат 1, чтобы закончить, но не любые внутренние вызовы делегатов):

Delegate 1 
End of Delegate 2 
End of Delegate 1 
Delegate 2 

Завершить вызов, пока второй делегат не завершит обработку als о? Или мне нужно будет использовать шаблоны вызова во всех делегированных вызовах?

+0

Не уверен в вашем синтаксисе для вашего отредактированного примера. Можете ли вы уточнить, что происходит с del1? – strager

ответ

7

Вы действительно можете использовать мониторы, семафоры, или вы можете даже подождать до тех пор, пока не будут выполнены ваши асинхронные вызовы.

Но вы можете получить это бесплатно. Если вы вызываете EndInvoke() делегата, который ранее был запущен с BeginInvoke(), вы блокируете до тех пор, пока не будет выполнена асинхронная работа.

Не уверен, что это помогает, потому что вы должны использовать этот асинхронный шаблон. Если это так, вы получаете бесплатное асинхронное выполнение (и переход от асинхронной обратной связи к синхронным вызовам).

Отъезд Calling Synchronous Methods Asynchronously on MSDN для получения дополнительной информации об этом шаблоне.

Надеюсь, это поможет!

+0

Это похоже на то, что мне нужно! Спасибо друг. –

2

Звучит так, как будто вы хотите, чтобы очередь ... если выполняется запрос, добавьте в очередь (с Monitor); когда звонок завершен, проверьте очередь ...

0

У вас есть несколько вариантов. Вот пара идей -

Прежде всего, вы можете использовать ManualResetEvent для «блокировки» основного потока, пока все ваши операции асинхронного действия не будут завершены. Идея состоит в том, чтобы просто вызвать вызов основного потока, который вы используете, а затем event.WaitOne(), и функция отвечает за настройку события.

В качестве альтернативы, вы можете попытаться использовать что-то вроде Semaphore. Он призван помочь в этих ситуациях, поскольку вы можете ограничить его до 10 одновременных событий, не беспокоясь о том, чтобы попытаться справиться с этим самостоятельно. Вероятно, это был мой предпочтительный подход.

0

Есть две причины для использования потоков:

  • , чтобы воспользоваться ресурсами центрального процессора, так ускоряя вещи
  • написать несколько одновременных государственных машин, как простые методы, что делает их более читаемыми

недостатком использования нитей:

  • это Reall y трудный

Если узкое место находится на расстоянии 3 минуты на удаленной машине, возможно, стоит отметить, что несколько потоков не принесут вам какого-либо преимущества в производительности. Вы можете написать целую однопоточную версию, поддерживая набор из десяти объектов «конечного автомата» и перемещая их по этапам создания пользователя, все из одного потока, с небольшой разницей во всей производительности.

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

Другой способ получить это через метод итератора, то есть содержащий yield return операторы, что также дает вам преимущества одночиповой читаемости. На прошлой неделе я должен иметь linked to this three times! Это статья Джеффри Рихтера об AsyncEnumerator.

2

IAsyncResult имеет свойство AsyncWaitHandle, которое дает вам дескриптор ожидания (если таковой имеется), который будет сигнализироваться при завершении операции.

Таким образом, вы можете использовать WaitHandle.WaitAll (или .WaitAny), чтобы выполнять беспроволочные ожидания на многих ручках одновременно.

0

Я немного не понимаю, как вы используете TransactionScope (так что я могу быть вне базы), но вы можете создавать DependentTransactions, которые могут быть добавлены к рабочим потокам. Ваша транзакция на верхнем уровне не будет выполняться до тех пор, пока все DependentTransactions не будут успешно выполнены, и наоборот с откатом. Я нашел это как простой способ делать все или ничего в потоках, пока операция, которую вы вызываете, инкапсулирует функцию фиксации/отката (IEnlistmentNotification или тому подобное).

+0

Вы правы, и если бы мы не ограничивались 10-ти максимальными соединениями для окончательного процесса, это было бы хорошо. К сожалению, мы ограничены 10 (на самом деле нам разрешено 2), поэтому их проще выполнять последовательно, а не поддерживать очередь. –

+0

Ограниченные параллельные соединения ... всегда веселое требование;) – jsw