为什么EventWaitHandle不起作用

本文关键字:不起作用 EventWaitHandle 为什么 | 更新日期: 2023-09-27 18:19:34

我有两个名为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)着色完成!(线程.CurrentThreadName=10)

我认为Thread.CurrentThreadName会有所不同,因为我使用了线程池!!!为什么?谢谢你的回答。

为什么EventWaitHandle不起作用

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;
    }
}

请参阅上面的代码片段,它应该会对您进行一些解释。当然,您有相同的线程名称,因为您将标签委托发送到UI线程-这是您不应该像最初那样在那里进行任何冗长操作的主要原因

关于您的问题(我在代码中看到了其他可能的问题),我会尝试在构造时设置事件处理程序,并删除Change_Text方法的waitMeHandle.Reset();

当您并行启动这两个进程时,您不能确定Change_Text是否会首先执行。

关于执行线程名称的问题:

如果调用Dispatcher.Invoke,则指定的委托将在Dispatcher关联的线程上执行。在您的情况下,可能是UI线程。

请参阅MSDN上的备注部分:

在WPF中,只有创建DispatcherObject的线程才能访问该对象。例如,从主UI线程分离出来的后台线程无法更新在UI线程上创建的Button的内容。为了让后台线程访问按钮的Content属性,后台线程必须将工作委托给与UI线程关联的Dispatcher。这是通过使用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";
    }

足以在ThreadPool线程上执行ChangeTextChangeColor