Синхронизировать связь потоков? - PullRequest
1 голос
/ 22 апреля 2010

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

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

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

Идея такова:

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

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

//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 голосов
/ 22 июля 2011

Есть много способов реализовать шаблон производителя / потребителя в C #.Я думаю, что лучший способ - использовать TPL (Task, BlockingCollection).Смотрите пример здесь .

...