多线程、BeginInvoke、多线程UI

dlutwy 2008-09-08 10:06:50
多线程、BeginInvoke到底有什么区别,请牛人给予解答,谢谢。
另外多线程UI操作要注意的问题。
...全文
1216 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
cc_net 2008-09-08
  • 打赏
  • 举报
回复
LZ问的这个问题太大了,建议先了先逐个了解清楚

多线程就是一个进程中使用多个线程,CPU执行时会轮流执行各个线程.
比如你只有一个主线程,当你后台在大量计算时,你的前台就有可能失去相应,因为你的UI主线程在计算,而无法进行重画.当计算完成后才能恢复
如果你使用了多线程,可以让主线程负责前台重画,另外的线程负责后台计算.对于单核CPU来说是采用一定策略轮流执行这2个线程,但对我们来说是透明的.因为轮换很快,所以宏观上看是同时在工作.

BeginInvoke的话就涉及到异步的问题了.异步实际是多线程的一种应用.具体也不是一两句话说的清楚的,看下MSDN吧.不过看这些之前,你得吧委托理解清楚.
gh_li 2008-09-08
  • 打赏
  • 举报
回复
路过帮顶
vwxyzh 2008-09-08
  • 打赏
  • 举报
回复
不知道lz说的BeginInvoke是指Control.BeginInvoke还是具体某委托的BeginInvoke
1、Control.BeginInvoke是将一个委托(说得底层一点就是函数指针)调用windows的api,post给ui线程(Control.Invoke是send给ui线程),无论哪种方式,执行委托的内容的线程都是ui线程
2、具体某委托的BeginInvoke实际上是将这个委托交给线程池,在线程池内执行
格拉 2008-09-08
  • 打赏
  • 举报
回复
学习
atlasroben 2008-09-08
  • 打赏
  • 举报
回复
多线程的操作一般根据你的需求而决定,我们用使用的线程数量来说
如果只是开启一个子线程进行一些操作,那么使用windowsform自带的backgroundworker比较好,因为他为你封装了大部分烦人的操作适合初学或者是简单的应用
如果是需要开启多个线程对这些线程进行控制那么会用到threadpool线程池;比如多线程上传文件等就会用到他

在winform中如果多线程操作ui那么你需要用到invokrequested属性,他是用来标识是否是跨线程访问的重要属性,因为在.net中,跨线程的直接访问ui是不安全的会用到委托

http://download.csdn.net/source/591777有下载示例演示关于winform中多线程操作ui

另外control.invoke与beginInvoke都是回到主线程操作,而不是begininvoke开启一个新的线程这点你需要注意他与delegate.begininvoke是有区别的
dlutwy 2008-09-08
  • 打赏
  • 举报
回复
我也能到网上搜到
ericzhangbo1982111 2008-09-08
  • 打赏
  • 举报
回复
BeginInvoke 方法真的是新开一个线程进行异步调用吗?

参考以下代码:

public delegate void treeinvoke();

private void UpdateTreeView()

{

MessageBox.Show(System.Threading.Thread.CurrentThread.Name);

}

private void button1_Click(object sender, System.EventArgs e)

{

System.Threading.Thread.CurrentThread.Name = "UIThread";

treeView1.BeginInvoke(new treeinvoke(UpdateTreeView));

}

看看运行结果,弹出的对话框中显示的是 UIThread,这说明 BeginInvoke 所调用的委托根本就是在 UI 线程中执行的。

既然是在 UI 线程中执行,又何来“异步执行”一说呢?

我们再看看下面的代码:

public delegate void treeinvoke();

private void UpdateTreeView()

{

MessageBox.Show(Thread.CurrentThread.Name);

}

private void button1_Click(object sender, System.EventArgs e)

{

Thread.CurrentThread.Name = "UIThread";

Thread th = new Thread(new ThreadStart(StartThread));

th.Start();

}

private void StartThread()

{

Thread.CurrentThread.Name = "Work Thread";

treeView1.BeginInvoke(new treeinvoke(UpdateTreeView));

}

再看看运行结果,弹出的对话框中显示的还是 UIThread,这说明什么?这说明 BeginInvoke 方法所调用的委托无论如何都是在 UI 线程中执行的。

那 BeginInvoke 究竟有什么用呢?

在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,具体的原因可以在看完我的这篇之后看看这篇:在多线程中如何调用Winform,如果你是大牛的话就不要看我这篇了,直接看那篇吧,反正那篇文章我没怎么看懂。

Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。

正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。

而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使 UI 线程的负担不至于太大而以,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。
很多时候写windows程序都需要结合多线程,在.net中用如下得代码来创建并启动一个新的线程。
public void ThreadProc();
Thread thread = new Thread( new ThreadStart( ThreadProc ) );
thread.IsBackground = true;
thread.Start();
但是很多时候,在新的线程中,我们需要与UI进行交互,在.net中不允许我们直接这样做。可以参考MSDN中的描述:
“Windows 窗体”使用单线程单元 (STA) 模型,因为“Windows 窗体”基于本机 Win32 窗口,而 Win32 窗口从本质上而言是单元线程。STA 模型意味着可以在任何线程上创建窗口,但窗口一旦创建后就不能切换线程,并且对它的所有函数调用都必须在其创建线程上发生。除了 Windows 窗体之外,.NET Framework 中的类使用自由线程模型。
STA 模型要求需从控件的非创建线程调用的控件上的任何方法必须被封送到(在其上执行)该控件的创建线程。基类 Control 为此目的提供了若干方法(Invoke、BeginInvoke 和 EndInvoke)。Invoke 生成同步方法调用;BeginInvoke 生成异步方法调用。
Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性。因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。
正如所看到的,我们必须调用Invoke方法,而BeginInvoke可以认为是Invoke的异步版本。调用方法如下:
public delegate void OutDelegate(string text);
public void OutText(string text)
{
txt.AppendText(text);
txt.AppendText( "\t\n" );
}
OutDelegate outdelegate = new OutDelegate( OutText );
this.BeginInvoke(outdelegate, new object[]{text});
如果我们需要在另外一个线程里面对UI进行操作,我们需要一个类似OutText的函数,还需要一个该函数的委托delegate,当然,这里展示的是自定义的,.net中还有很多其他类型的委托,可以直接使用,不需要而外声明。例如:MethodInvoker和EventHandler,这两种类型委托的函数外观是固定的,MethodInvoker是void Function()类型的委托,而EventHandler是void Function(object, EventArgs)类型的委托,第一个不支持参数,第二中的参数类型和数量都是固定的,这两种委托可以很方便的调用,但是缺乏灵活性。请注意BeginInvoke前面的对象是this,也就是主线程。现在再介绍Control.InvokeRequired,Control是所有控件的基类,对于这个属性MSDN的描述是:
获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。
也就是说通过判断InvokeRequired可以知道是否需要用委托来调用当前控件的一些方法,如此可以把OutText函数修改一下:
public delegate void OutDelegate(string text);
public void OutText(string text)
{
if( txt.InvokeRequired )
{
OutDelegate outdelegate = new OutDelegate( OutText );
this.BeginInvoke(outdelegate, new object[]{text});
return;
}
txt.AppendText(text);
txt.AppendText( "\t\n" );
}
注意,这里的函数没有返回,如果有返回,需要调用Invoke或者EndInvoke来获得返回的结果,不要因为包装而丢失了返回值。如果调用没有完成,Invoke和EndInvoke都将会引起阻塞。
现在如果我有一个线程函数如下:
public void ThreadProc()
{
for(int i = 0; i < 5; i++)
{
OutText( i.ToString() );
Thread.Sleep(1000);
}
}
如果循环的次数很大,或者漏了Thread.Sleep(1000);,那么你的UI肯定会停止响应,想知道原因吗?看看BeginInvoke前面的对象,没错,就是this,也就是主线程,当你的主线程不停的调用OutText的时候,UI当然会停止响应。

与以前VC中创建一个新的线程需要调用AfxBeginThread函数,该函数中第一个参数就是线程函数的地址,而第二个参数是一个类型为LPVOID的指针类型,这个参数将传递给线程函数。现在我们没有办法再使用这种方法来传递参数了。我们需要将传递给线程的参数和线程函数包装成一个单独的类,然后在这个类的构造函数中初始化该线程所需的参数,然后再将该实例的线程函数传递给Thread类的构造函数。代码大致如下:
public class ProcClass
{
private string procParameter = "";
public ProcClass(string parameter)
{
procParameter = parameter;
}
public void ThreadProc()
{
}
}
ProcClass threadProc = new ProcClass("use thread class");
Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
thread.IsBackground = true;
thread.Start();
就是这样,需要建立一个中间类来传递线程所需的参数。
那么如果我的线程又需要参数,又需要和UI进行交互的时候该怎么办呢?可以修改一下代码:
public class ProcClass
{
private string procParameter = "";
private Form1.OutDelegate delg = null;
public ProcClass(string parameter, Form1.OutDelegate delg)
{
procParameter = parameter;
this.delg = delg;
}
public void ThreadProc()
{
delg.BeginInvoke("use ProcClass.ThreadProc()", null, null);
}
}
ProcClass threadProc = new ProcClass("use thread class", new OutDelegate(OutText));
Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
thread.IsBackground = true;
thread.Start();

110,476

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧