Асинхронные/синхронные одновременные вызовы с очередями

У меня есть сценарий, в котором я выполняю некую очередь сообщений типа Actor-Model, где я хочу, чтобы метод вставлял Task или делегировал в очередь (возможно, новый ConcurrentQueue), ждал, пока какой-то другой процесс обработает очередь, выполните задачу а затем вернуть результат, желательно без блокировки. Метод может вызываться как синхронно, так и асинхронно. Одновременно может выполняться только одно действие в очереди

Я не могу понять, как сделать это несколько эффективным образом, пожалуйста, помогите :)

ИЗМЕНИТЬ

Вот попытка, кто-нибудь видел какие-либо проблемы с этим подходом (обработка исключений исключена)? Кроме того, я могу себе представить, что это имеет довольно много накладных расходов по сравнению с простой блокировкой, и как это соотносится, например, с использованием асинхронных делегатов?

  public partial class Form1 : Form
  {
    private BlockingCollection<Task<int>> blockingCollection = new BlockingCollection<Task<int>>(new ConcurrentQueue<Task<int>>());
    private int i = 0;
    public Form1() {
      InitializeComponent();

      Task.Factory.StartNew(() =>
      {
          foreach (var task in blockingCollection.GetConsumingEnumerable()) {
            task.Start();
            task.Wait();        
          }
      });
    }


    public int Queue() {
      var task = new Task<int>(new Func<int>(DoSomething));
      this.blockingCollection.Add(task);
      task.Wait();
      return task.Result;
    }

    public int DoSomething() {
      return Interlocked.Increment(ref this.i);
    }

    private void button1_Click(object sender, EventArgs e) {
      Task.Factory.StartNew(() => Console.Write(this.Queue()));
    }


  }

person Homde    schedule 08.04.2011    source источник
comment
Каков разрыв между параллельной библиотекой TPL Task из фреймворка и вашими требованиями? Очередь просто обрабатывается фреймворком, и в зависимости от ваших требований существуют различные варианты синхронизации.   -  person weismat    schedule 08.04.2011
comment
Вы можете взглянуть на библиотеку SmartThreadPool (smartthreadpool.codeplex.com), она может соответствовать вашим потребностям.   -  person Larry    schedule 08.04.2011


Ответы (1)


TPL должен сделать это за вас — просто вызовите Wait() на своем Task<T> — однако это невозможно сделать без блокировки; по определению, в вашем сценарии это именно то, что вы хотите сделать. Блокировка может быть реализована через lock, но есть и другие способы - TPL скрывает это. Лично я в аналогичном сценарии делаю это с помощью пользовательской очереди и мини-пула объектов, которые я могу использовать для блокировки (никогда не выставляемых за пределы оболочки).

Вы также можете взглянуть на материал async/await C# 5.

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

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

person Marc Gravell    schedule 08.04.2011