我有两个名为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 !!!为什么?谢谢你的答案。
答案 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线程上执行ChangeText
和ChangeColor
。