почему EventWaitHandle не работает? - PullRequest
0 голосов
/ 05 января 2012

У меня есть две функции с именем ChangeText () и ChangeColor (), первая функция с именем ChangeText, которая будет загружать большое количество данных в память, это будет стоить много времени, поэтому я запускаю ее асинхронно; другая называется ChangeColor, которая изменит цвет кнопки при нормальной загрузке данных, поэтому существует порядок запуска этих двух функций: сначала ChangeText, а затем ChangeColor. вот мой код:

using System;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Threading;
using System.IO;

namespace ThreadSynchorous
{
    public partial class Window1 : Window
    {
        public Window1()
        {
        InitializeComponent();
        asyncInvoke = new AsyncInvoke();
    }
    AsyncInvoke asyncInvoke;
    EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset);

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeText);
        }), null);

        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeColor);
        }), null);

        label1.Content += " \r\n-------------------------\r\n";
    }

    private bool ChangeText()
    {
        waitMeHandle.Reset();
        this.button1.Dispatcher.Invoke(new Func<bool>(delegate()
        {
            string filename = @"C:\EXO.txt";
            using (StreamReader sr = new StreamReader(filename, Encoding.Default))
            {
                string result;
                while ((result = sr.ReadLine()) != null)
                {
                    //here perform action
                }
            }

            label1.Dispatcher.Invoke(new Func<bool>(delegate
            {
                label1.Content += "Loading finish!(Thread.CurrentThreadName="+Thread.CurrentThread.ManagedThreadId.ToString()+") ";
                waitMeHandle.Set();
                return true;
            }));
            waitMeHandle.Set();
            return true;
        }));
        waitMeHandle.Set();
        return true;
    }

    private bool ChangeColor()
    {
        waitMeHandle.WaitOne();
        this.button1.Dispatcher.Invoke(new Func<bool>(delegate()
        {
            this.button1.Background = Brushes.Red;

            label1.Dispatcher.Invoke(new Func<bool>(delegate()
            {
                label1.Content += "Coloring finish!(Thread.CurrentThreadName="+Thread.CurrentThread.ManagedThreadId+") ";
                return true;
            }));

            return true;
        }));
        return true;
    }
}
}

вот класс AsyncInvoke:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ThreadSynchorous
{
    public  class AsyncInvoke
    {
        public void BeginAsync(Func<bool> MyFunction)
        {
            Func<bool> func = new Func<bool>(MyFunction);
            IAsyncResult iar = func.BeginInvoke(new AsyncCallback(EndAsync), func);
        }

        public void EndAsync(IAsyncResult iar)
        {
            Func<bool> func = (Func<bool>)iar.AsyncState;
            func.EndInvoke(iar);
        }
    }
}

Я планировал использовать EventWaitHandle для синхронизации этих двух функций, но в результате эти две функции будут по-прежнему работать в беспорядочном порядке: иногда сначала функция ChangeText (), а потом сначала ChangeColor (). Я просто так растерялся.

Кроме того, я использую ThreadPool для запуска этих двух функций, но почему я получил такой же ThreadID, как показано ниже:
Загрузка завершена! (Thread.CurrentThreadName = 10) Завершение раскраски! (Thread.CurrentThreadName = 10)

Я думал, что Thread.CurrentThreadName будет другим, потому что я использую пул потоков !!! Зачем? спасибо за ваш ответ.

Ответы [ 3 ]

0 голосов
/ 05 января 2012
public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        asyncInvoke = new AsyncInvoke();
    }
    AsyncInvoke asyncInvoke;
    EventWaitHandle waitMeHandle = new EventWaitHandle(false, EventResetMode.ManualReset);

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeText);
        }), null);

        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeColor);
        }), null);

        label1.Content += " \r\n-------------------------\r\n";
    }

    private bool ChangeText()
    {
        Debug.WriteLine("ChangeText");         

        //do your time-consuming operation here, controls' delegated are for UI updates only

        this.button1.Dispatcher.Invoke((Action)(()=>
        {
            Thread.Sleep(2000);
            Debug.WriteLine("Button invoker");
            //update button here


            //what was bool return type for?
            label1.Dispatcher.Invoke((Action)(() =>
            {
                label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") ";
                waitMeHandle.Set();
            }));

        }));


        //waitMeHandle.Set(); - here's your guilty - button delegate runs asynchrounously so you had absolutely no guarantee that it's done as your app reach this line
        return true;
    }

    private bool ChangeColor()
    {
        waitMeHandle.WaitOne();
        Debug.WriteLine("ChangeColor");
        this.button1.Dispatcher.Invoke((Action)(() =>
        {
            this.button1.Background = Brushes.Red;

            label1.Dispatcher.Invoke((Action)(() =>
            {
                label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") ";
                waitMeHandle.Reset(); //you've consumed your event here so this is the place to reset it
            }));
        }));
        return true;
    }
}

См. Фрагмент кода выше - это должно объяснить вам немного.И, конечно же, у вас есть то же имя потока, потому что вы отправляете делегат метки в поток пользовательского интерфейса - это основная причина, по которой вам не следует выполнять никаких длительных операций, как вы делали изначально

0 голосов
/ 05 января 2012

Что касается вопроса об имени исполняющей нити:

Если вы вызовете Dispatcher.Invoke, указанный делегат будет выполнен в потоке, с которым связан Диспетчер. В вашем случае, вероятно, поток пользовательского интерфейса.

См. Раздел замечаний по MSDN :

В WPF только поток, создавший DispatcherObject, может получить доступ к этому объекту. Например, фоновый поток, выделенный из основного потока пользовательского интерфейса, не может обновить содержимое кнопки, созданной в потоке пользовательского интерфейса. Чтобы фоновый поток мог получить доступ к свойству Content кнопки, фоновый поток должен делегировать работу диспетчеру, связанному с потоком пользовательского интерфейса. Это достигается с помощью Invoke или BeginInvoke.

Далее, вы перестарались. Если вы вызываете ThreadPool.QueueUserWorkItem, вы планируете выполнение делегата в потоке ThreadPool. Теперь в вашем коде, если в методе, который выполняется в потоке ThreadPool, вы вызываете Func<T>.BeginInvoke, тогда вы снова планируете выполнение делегата в потоке ThreadPool. Так что просто изменив свой код на это:

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(o => ChangeText());

        ThreadPool.QueueUserWorkItem(o => ChangeColor());

        label1.Content += " \r\n-------------------------\r\n";
    }

Достаточно для выполнения ChangeText и ChangeColor в потоках ThreadPool.

0 голосов
/ 05 января 2012

Относительно вашего вопроса (я вижу другие возможные проблемы в коде), я бы попытался установить обработчик событий на конструкцию и удалить waitMeHandle.Reset(); метода Change_Text.

Поскольку вы запускаете два процесса параллельно, вы не можете быть уверены, что Change_Text будет выполнен первым.

...