2010-04-22 3 views
1

Просто для этого я пытаюсь подражать тому, как генераторы JRuby работают, используя потоки в C#.Синхронизация связи потоков?

Кроме того, я полностью отдаю себе отчет в том, что C# имеет встроенную поддержку возврата доходности, я просто немного кручусь.

Я думаю, это какой-то бедный mans coroutines, удерживая несколько призов в живых с помощью потоков. (Даже если ни один из callstacks не должны выполняться одновременно)

Идея такова:

  • Потребитель поток запрашивает значение
  • Рабочий поток обеспечивает значение и дает обратно в потребитель нить
  • Повторите до тех пор пока рабочий поток выполняется

Итак, что было бы правильным способом сделать следующее?

//example 
class Program 
{ 
    static void Main(string[] args) 
    { 
     ThreadedEnumerator<string> enumerator = new ThreadedEnumerator<string>(); 

     enumerator.Init(() => 
      { 
       for (int i = 1; i < 100; i++) 
       { 
        enumerator.Yield(i.ToString()); 
       } 
      }); 

     foreach (var item in enumerator) 
     { 
      Console.WriteLine(item); 
     }; 

     Console.ReadLine(); 
    } 
} 

//naive threaded enumerator 
public class ThreadedEnumerator<T> : IEnumerator<T>, IEnumerable<T> 
{ 
    private Thread enumeratorThread; 
    private T current; 
    private bool hasMore = true; 
    private bool isStarted = false; 
    AutoResetEvent enumeratorEvent = new AutoResetEvent(false); 
    AutoResetEvent consumerEvent = new AutoResetEvent(false); 
    public void Yield(T item) 
    { 
     //wait for consumer to request a value 
     consumerEvent.WaitOne(); 

     //assign the value 
     current = item; 

     //signal that we have yielded the requested 
     enumeratorEvent.Set(); 
    } 

    public void Init(Action userAction) 
    { 
     Action WrappedAction =() => 
     { 
      userAction(); 
      consumerEvent.WaitOne(); 
      enumeratorEvent.Set(); 
      hasMore = false; 
     }; 
     ThreadStart ts = new ThreadStart(WrappedAction); 
     enumeratorThread = new Thread(ts); 
     enumeratorThread.IsBackground = true; 
     isStarted = false; 
    } 

    public T Current 
    { 
     get { return current; } 
    } 

    public void Dispose() 
    { 
     enumeratorThread.Abort(); 
    } 

    object System.Collections.IEnumerator.Current 
    { 
     get { return Current; } 
    } 

    public bool MoveNext() 
    { 
     if (!isStarted) 
     { 
      isStarted = true; 
      enumeratorThread.Start(); 
     } 
     //signal that we are ready to receive a value 
     consumerEvent.Set(); 

     //wait for the enumerator to yield 
     enumeratorEvent.WaitOne(); 

     return hasMore; 
    } 

    public void Reset() 
    { 
     throw new NotImplementedException(); 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return this; 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return this; 
    } 
} 

Идеи?

+1

Ваш код выглядит в целом правильным. Однако я не уделял слишком много времени, так что могут быть небольшие ошибки, но в целом хорошо выглядит. Вы видите какие-то проблемы с этим? Каков твой вопрос? –

+0

Основной вопрос: есть ли лучший способ выполнить то же самое без использования встроенных машин возврата C# yield ?. Что касается кода, он зависает в некоторых случаях и работает крайне плохо, если вы вставляете счетчики. Я не знаю, когда дело доходит до резьбы, поэтому я не совсем понимаю, что делать дальше –

ответ

0

Существует множество способов реализации шаблона производителя/потребителя в C#. Лучший способ, я думаю, использовать TPL (Task, BlockingCollection). См. Пример here.

Смежные вопросы