C# Многопоточность - что не так, как использовать AutoResetEvent - PullRequest
0 голосов
/ 16 апреля 2020

Я все еще изучаю Threading, и у меня есть проблемы с этим кодом ниже. Извините, если этот вопрос появился раньше, я просто не понимаю, почему этот код не работает. Я упростил код:

    static EventWaitHandle waitH;      // AutoResetEvent, wait for signal
    static bool whExit;                // signal to exit waiting
    static Queue<string> str;          // waiting line (example values)
    static Queue<int> num;             // 

    static void Main(string[] args)
    {
        waitH = new AutoResetEvent(false);    // initialize waiter
        str = new Queue<string>();
        num = new Queue<int>();

        Thread thr = new Thread(new ThreadStart(Waiter));    // waiting in another thread
        thr.Start();                                         // start the waiting thread

        for(short i = 0; i < 10; i++)
        {
            str.Enqueue(string.Format($"{(char)(i + 65)}"));    // add something to queue
            num.Enqueue(i);    // add a number to test incrementing
            waitH.Set();    // signal to start the "long processing"
        }
    }

    static void Waiter()
    {
        while(!whExit)
        {
            waitH.WaitOne();    // wait for signal

            WriteToConsole();    // start the long processing on another thread
        }
    }

    static void WriteToConsole()
    {
        // threadstart with parameters
        // action: void delegate
        // get 2 values from waiting line
        var f = new ParameterizedThreadStart(obj =>
           new Action<string, int>(ConsoleWriter)
           (str.Dequeue(), num.Dequeue()));          // it's thread safe, because FIFO?

        Thread thr = new Thread(f);
        thr.IsBackground = true;           // close thread when finished
        thr.Start();
    }

    // print to console
    static void ConsoleWriter(string s, int n)
    {
        Console.WriteLine(string.Format($"{s}: {++n}"));     // easy example
    }

Останавливается в главном l oop. Я думаю, что проблема в том, что Thread.Start () вызывается первым, но ему нужно изменить состояние потока и присоединиться к очереди «необходимо обработать», что требует времени. L oop Main работает уже и не ожидает сигнала.

Я решил эту проблему с помощью двусторонней сигнализации: использовал другой сигнал паузы AutoResetEvent после waitH.Set () в l oop ( WaitOne) и подать сигнал после завершения Console.WriteLine ().

Я не очень горжусь этим решением, потому что если я это сделаю, программа теряет "threadi sh", параллельный или синхронный подходить. И это пример, я хотел бы запускать длинные вычисления одновременно в разных потоках.

Если я увижу вывод, это пример для книги, который я делаю неправильно:

Вывод: A: 1 B: 2 Иногда B: 2 A: 1

Ожидаемый вывод: A: 1 B: 2 C: 3 D: 4 E: 5 F: 6 G: 7 H: 8 I: 9 J: 10

Есть ли какой-нибудь элегантный способ решить эту проблему? Может быть, использовать замки и т. Д. c.

Любое обсуждение будет приветствоваться. Спасибо!

1 Ответ

1 голос
/ 16 апреля 2020

Существует несколько проблем:

  1. Основной метод никогда не ожидает завершения рабочего потока, поэтому он, вероятно, запустится до завершения и остановит все потоки, прежде чем они будут завершены. Эту проблему можно решить, сигнализируя рабочему потоку об остановке, а затем используйте thread.Join(), чтобы дождаться его завершения.

  2. WriteToConsole берет один элемент из каждого списка и печатает его в консоль. Но поток может начаться после завершения l oop в основном методе. Поэтому, когда поток запускается, событие autoReset будет сигнализировано, и один элемент будет обработан. Но на следующей итерации autoResetEvent не будет сигнализироваться и никогда не станет сигнализироваться снова. Эту проблему можно решить путем перебора всех элементов в очереди после того, как событие было сигнализировано.

  3. Использование двусторонней сигнализации в l oop, как вы упомянули, фактически сериализует код, удаляя любое преимущество использования потоков.
  4. Если это учебное упражнение, я бы посоветовал потратить время на изучение Задачи , async / await , lock и Parallel.For в первую очередь. Если у вас есть хорошая гр asp этих вещей, вы будете гораздо более эффективны, чем использование потоков и сброс событий вручную.

// это потокобезопасно, потому что FIFO?

Нет. Используйте одновременных коллекций , если вы хотите потокобезопасные коллекции.

...