为什么EventWaitHandle不起作用?

时间:2012-01-05 08:24:33

标签: c# threadpool

我有两个名为ChangeText()&的函数。 ChangeColor(),第一个名为ChangeText的函数,它会将大量数据加载到内存中,这将耗费大量时间,所以我异步运行它;另一个叫做ChangeColor,当数据加载时会改变按钮的颜色,所以有一个命令来运行这两个函数:ChangeText first和ChangeColor second。这是我的代码:

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会有所不同,因为我使用了threadpool !!!为什么?谢谢你的答案。

3 个答案:

答案 0 :(得分:0)

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

当你并行启动这两个进程时,你无法确定是否会先执行Change_Text

答案 1 :(得分:0)

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线程 - 这是你不应该像最初那样在那里进行任何冗长操作的主要原因

答案 2 :(得分:0)

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

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

请参阅MSDN上的备注部分:

  

在WPF中,只有创建DispatcherObject的线程才能访问该对象。例如,从主UI线程分离出来的后台线程无法更新在UI线程上创建的Button的内容。为了让后台线程访问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