Планирование с помощью EventWaitHandle с помощью Dispatcher.BeginInvoke

Следующий фрагмент кода имеет два потока, каждый из которых пишет 20 string str в соответствующее текстовое поле. После завершения, Thread t00 сигналы Thread t01 начать и изменить общий string str от у до х. Thread t00 должен написать 20 лет в текстовое поле, и Thread t01 следует написать 20 х в другое текстовое поле. Вместо, Thread t00 в итоге пишу 19 лет и 1 х. Но если я добавлю Thread.Sleep() до того, как EventWaitHandle установлен, это решает проблему, которую я имел (я получаю 20 х и 20 лет), но почему? EventWaitHandle не должен быть установлен только после окончания цикла, с или без Thread.Sleep(),

public partial class MainWindow : Window
{
    public string str = "y";
    static EventWaitHandle _waitHandle = new AutoResetEvent(false);

    public MainWindow()
    {
        InitializeComponent();

        Thread t00 = new Thread(() =>
        {
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Action action00 = () =>
                {
                    tb00.AppendText(str);
                };
                Dispatcher.BeginInvoke(action00);
            }
            Thread.Sleep(200);  // <-- why this fix the problem??
            _waitHandle.Set();
        });
        t00.Start();


        Thread t01 = new Thread(() =>
        {
            Action action00 = () =>
            {
                tb01.AppendText("Waiting...\n");
            };
            Dispatcher.BeginInvoke(action00);
            _waitHandle.WaitOne();

            str = "x";
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Action action = () =>
                {
                    tb01.AppendText(str);
                };
                Dispatcher.BeginInvoke(action);
            }
        });
        t01.Start();
    }
}

введите описание изображения здесь

2 ответа

Решение

Потому что вы используете BeginInvoke, BeginInvoke вызывает делегат в потоке пользовательского интерфейса асинхронно. Он помещает сообщение в очередь сообщений потока пользовательского интерфейса и возвращает его, поэтому тот факт, что он возвратил, не означает, что действие было фактически выполнено.

По этой причине, в большинстве случаев, когда вы устанавливаете дескриптор ожидания, а другой поток получает Singal и изменения str от y в x - есть еще не вызванный tb00.AppendText(str); делегировать в очереди потока пользовательского интерфейса. Когда это наконец вызывается - str уже x,

Thread.Sleep "исправляет" это, потому что дает некоторое время для выполнения этого ожидающего делегата в потоке пользовательского интерфейса. С помощью Invoke вместо BeginInvoke также "исправляет" это, потому что Invoke является синхронным и возвращается только после фактического выполнения делегата в потоке пользовательского интерфейса.

Думать о BeginInvoke() толкая единицу работы в очередь. Когда 20-го action00 выталкивается из рабочей очереди и выполняется, str='x' уже выполнено, и поэтому, когда action00 на самом деле запустить, он напечатает x,

Thread.Sleep(200) дает 20 action00 время выскочить и бежать до str='x' выполнить, следовательно, он напечатает y,

Другие вопросы по тегам