Thread的问题
C#是一门支持多线程的语言,因此线程的使用也是比较常见的。由于线程的知识在Win32编程的时候已经说得过多,所以在.Net中很少介绍这部分(可能.Net不觉得这部分是它所特有的)。
那么线程相关的问题大致有如下四类(这篇文章只讨论单线程、单线程与UI线程这两方面的问题)。
问题一,线程的基本操作,例如:暂停、继续、停止等;
问题二,如何向线程传递参数或者从中得到其返回值;
问题三,如何使线程所占用的CPU不要老是百分之百;
最后一个,也是问题最多的,就是如何在子线程来控制UI中的控件,换句话说,就是在线程中控制窗体某些控件的显示。
对于问题一,我不建议使用Thread类提供的Suspend、Resume以及Abort这三个方法,前两个有问题,好像在VS05已经屏蔽这两个方法;对于Abort来说,除了资源没有得到及时释放外,有时候会出现异常。如何做呢,通过设置开关变量来完成。
对于问题二,我不建议使用静态成员来完成,仅仅为了线程而破坏类的封装有些得不偿失。那如何做呢,通过创建单独的线程类来完成。
对于问题三来说,造成这个原因是由于线程中进行不间断的循环操作,从而使CPU完全被子线程占有。那么处理此类问题,其实很简单,在适当的位置调用Thread.Sleep(20)来释放所占有CPU资源,不要小看这20毫秒的睡眠,它的作用可是巨大的,可以使其他线程得到CPU资源,从而使你的CPU使用效率降下来。
看完前面的三个问题的解释,对于如何做似乎没有给出一个明确的答案,为了更好地说明如何解决这三个问题,我用一个比较完整的例子展现给大家,代码如下。
//--------------------------- Sub-thread class ---------------------------------------
//------------------------------------------------------------------------------------
//---File: clsSubThread
//---Description: The sub-thread template class file
//---Author: Knight
//---Date: Aug.21, 2006
//------------------------------------------------------------------------------------
//---------------------------{Sub-thread class}---------------------------------------
namespace ThreadTemplate
{
using System;
using System.Threading;
using System.IO;
/// <summary>
/// Summary description for clsSubThread.
/// </summary>
public class clsSubThread:IDisposable
{
private Thread thdSubThread = null;
private Mutex mUnique = new Mutex();
private bool blnIsStopped;
private bool blnSuspended;
private bool blnStarted;
private int nStartNum;
public bool IsStopped
{
get{ return blnIsStopped; }
}
public bool IsSuspended
{
get{ return blnSuspended; }
}
public int ReturnValue
{
get{ return nStartNum;}
}
public clsSubThread( int StartNum )
{
//
// TODO: Add constructor logic here
//
blnIsStopped = true;
blnSuspended = false;
blnStarted = false;
nStartNum = StartNum;
}
/// <summary>
/// Start sub-thread
/// </summary>
public void Start()
{
if( !blnStarted )
{
thdSubThread = new Thread( new ThreadStart( SubThread ) );
blnIsStopped = false;
blnStarted = true;
thdSubThread.Start();
}
}
/// <summary>
/// Thread entry function
/// </summary>
private void SubThread()
{
do
{
// Wait for resume-command if got suspend-command here
mUnique.WaitOne();
mUnique.ReleaseMutex();
nStartNum++;
Thread.Sleep(1000); // Release CPU here
}while( blnIsStopped == false );
}
/// <summary>
/// Suspend sub-thread
/// </summary>
public void Suspend()
{
if( blnStarted && !blnSuspended )
{
blnSuspended = true;
mUnique.WaitOne();
}
}
/// <summary>
/// Resume sub-thread
/// </summary>
public void Resume()
{
if( blnStarted && blnSuspended )
{
blnSuspended = false;
mUnique.ReleaseMutex();
}
}
/// <summary>
/// Stop sub-thread
/// </summary>
public void Stop()
{
if( blnStarted )
{
if( blnSuspended )
Resume();
blnStarted = false;
blnIsStopped = true;
thdSubThread.Join();
}
}
#region IDisposable Members
/// <summary>
/// Class resources dispose here
/// </summary>
public void Dispose()
{
// TODO: Add clsSubThread.Dispose implementation
Stop();//Stop thread first
GC.SuppressFinalize( this );
}
#endregion
}
}
那么对于调用呢,就非常简单了,如下:
// Create new sub-thread object with parameters
clsSubThread mySubThread = new clsSubThread( 5 );
mySubThread.Start();//Start thread
Thread.Sleep( 2000 );
mySubThread.Suspend();//Suspend thread
Thread.Sleep( 2000 );
mySubThread.Resume();//Resume thread
Thread.Sleep( 2000 );
mySubThread.Stop();//Stop thread
//Get thread's return value
Debug.WriteLine( mySubThread.ReturnValue );
//Release sub-thread object
mySubThread.Dispose();
在回过头来看看前面所说的三个问题。
对于问题一来说,首先需要局部成员的支持,那么
private Mutex mUnique = new Mutex();
private bool blnIsStopped;
private bool blnSuspended;
private bool blnStarted;
光看成员名称,估计大家都已经猜出其代表的意思。接下来需要修改线程入口函数,要是这些开关变量能发挥作用,那么看看SubThread这个函数。
/// <summary>
/// Thread entry function
/// </summary>
private void SubThread()
{
do
{
// Wait for resume-command if got suspend-command here
mUnique.WaitOne();
mUnique.ReleaseMutex();
nStartNum++;
Thread.Sleep(1000);
}while( blnIsStopped == false );
}
函数比较简单,不到十句,可能对于“blnIsStopped == false”这个判断来说,大家还比较好理解,这是一个普通的判断,如果当前Stop开关打开了,就停止循环;否则一直循环。
大家比较迷惑的可能是如下这两句:
mUnique.WaitOne();
mUnique.ReleaseMutex();
这两句的目的是为了使线程在Suspend操作的时候能发挥效果,为了解释这两句,需要结合Suspend和Resume这两个方法,它俩的代码如下。
/// <summary>
/// Suspend sub-thread
/// </summary>
public void Suspend()
{
if( blnStarted && !blnSuspended )
{
blnSuspended = true;
mUnique.WaitOne();
}
}
/// <summary>
/// Resume sub-thread
/// </summary>
public void Resume()
{
if( blnStarted && blnSuspended )
{
blnSuspended = false;
mUnique.ReleaseMutex();
}
}
为了更好地说明,还需要先简单说说Mutex类型。对于此类型对象,当调用对象的WaitOne之后,如果此时没有其他线程对它使用的时候,就立刻获得信号量,继续执行代码;当再调用ReleaseMutex之前,如果再调用对象的WaitOne方法,就会一直等待,直到获得信号量的调用ReleaseMutex来进行释放。这就好比卫生间的使用,如果没有人使用则可以直接使用,否则只有等待。
明白了这一点后,再来解释这两句所能出现的现象。
mUnique.WaitOne();
mUnique.ReleaseMutex();
当在线程函数中,执行到“mUnique.WaitOne();”这一句的时候,如果此时外界没有发送Suspend消息,也就是信号量没有被占用,那么这一句可以立刻返回。那么为什么要紧接着释放呢,因为不能总占着信号量,立即释放信号量是避免在发送Suspend命令的时候出现等待;如果此时外界已经发送了Suspend消息,也就是说信号量已经被占用,此时“mUnique.WaitOne();”不能立刻返回,需要等到信号量被释放才能继续进行,也就是需要调用Resume的时候,“mUnique.WaitOne();”才能获得信号量进行继续执行。这样才能达到真正意义上的Suspend和Resume。
至于线程的Start和Stop来说,相对比较简单,这里我就不多说了。
现在再来分析一下问题二,其实例子比较明显,是通过构造函数和属性来完成参数和返回值,这一点我也不多说了。如果线程参数比较多的话,可以考虑属性来完成,类似于返回值。
问题三,我就更不用多说了。有人说了,如果子线程中的循环不能睡眠怎么办,因为睡眠的话,有时会造成数据丢失,这方面的可以借鉴前面Suspend的做法,如果更复杂,则牵扯到多线程的同步问题,这部分我会稍后单独写一篇文章。
前三个问题解决了,该说说最常见的问题,如何在子线程中控制窗体控件。这也是写线程方面程序经常遇到的,其实我以前写过两篇文章,都对这方面做了部分介绍。那么大家如果有时间的话,不妨去看看。
http://blog.csdn.net/knight94/archive/2006/03/16/626584.aspx
http://blog.csdn.net/knight94/archive/2006/05/27/757351.aspx
首先说说,为什么不能直接在子线程中操纵UI呢。原因在于子线程和UI线程属于不同的上下文,换句比较通俗的话说,就好比两个人在不同的房间里一样,那么要你直接操作另一个房间里的东西,恐怕不行罢,那么对于子线程来说也一样,不能直接操作UI线程中的对象。
那么如何在子线程中操纵UI线程中的对象呢,.Net提供了Invoke和BeginInvoke这两种方法。简单地说,就是子线程发消息让UI线程来完成相应的操作。
这两个方法有什么区别,这在我以前的文章已经说过了,Invoke需要等到所调函数的返回,而BeginInvoke则不需要。
用这两个方法需要注意的,有如下三点:
第一个是由于Invoke和BeginInvoke属于Control类型的成员方法,因此调用的时候,需要得到Control类型的对象才能触发,也就是说你要触发窗体做什么操作或者窗体上某个控件做什么操作,需要把窗体对象或者控件对象传递到线程中。
第二个,对于Invoke和BeginInvoke接受的参数属于一个delegate类型,我在以前的文章中使用的是MethodInvoker,这是.Net自带的一个delegate类型,而并不意味着在使用Invoke或者BeginInvoke的时候只能用它。参看我给的第二篇文章(《如何弹出一个模式窗口来显示进度条》),会有很多不同的delegate定义。
最后一个,使用Invoke和BeginInvoke有个需要注意的,就是当子线程在Form_Load开启的时候,会遇到异常,这是因为触发Invoke的对象还没有完全初始化完毕。处理此类问题,在开启线程之前显式的调用“this.Show();”,来使窗体显示在线程开启之前。如果此时只是开启线程来初始化显示数据,那我建议你不要使用子线程,用Splash窗体的效果可能更好。这方面可以参看如下的例子。
http://www.syncfusion.com/FAQ/WindowsForms/FAQ_c95c.aspx#q621q
线程的四个相关问题已经说完了,这篇文章只说了单线程,以及单线程与UI线程交互的问题。其中涉及到的方法不一定是唯一的,因为.Net还提供了其他类来扶助线程操作,这里就不一一罗列。至于多线程之间的同步,我会稍后专门写篇文章进行描述。
如何在子线程中操作窗体上的控件
一般来说,直接在子线程中对窗体上的控件操作是会出现异常,这是由于子线程和运行窗体的线程是不同的空间,因此想要在子线程来操作窗体上的控件,是不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了Invoke的方法,其作用就是让子线程告诉窗体线程来完成相应的控件操作。
现在用一个用线程控制的进程条来说明,大致的步骤如下:
1. 创建Invoke函数,大致如下:
/// <summary>
/// Delegate function to be invoked by main thread
/// </summary>
private void InvokeFun()
{
if( prgBar.Value < 100 )
prgBar.Value = prgBar.Value + 1;
}
2. 子线程入口函数:
/// <summary>
/// Thread function interface
/// </summary>
private void ThreadFun()
{
//Create invoke method by specific function
MethodInvoker mi = new MethodInvoker( this.InvokeFun );
for( int i = 0; i < 100; i++ )
{
this.BeginInvoke( mi );
Thread.Sleep( 100 );
}
}
3. 创建子线程:
Thread thdProcess = new Thread( new ThreadStart( ThreadFun ) );
thdProcess.Start();
备注:
using System.Threading;
private System.Windows.Forms.ProgressBar prgBar;
运行后的效果如下图所示:
如何弹出一个模式窗口来显示进度条
最近看了好多人问这方面的问题,以前我也写过一篇blog,里面说了如何在子线程中控制进度条。但目前大多数环境,需要弹出模式窗口,来显示进度条,那么只需要在原先的基础上稍作修改即可。
首先是进度条窗体,需要在上面添加进度条,然后去掉ControlBox。除此外,还要增加一个方法,用来控制进度条的增加幅度,具体如下:
/// <summary>
/// Increase process bar
/// </summary>
/// <param name="nValue">the value increased</param>
/// <returns></returns>
public bool Increase( int nValue )
{
if( nValue > 0 )
{
if( prcBar.Value + nValue < prcBar.Maximum )
{
prcBar.Value += nValue;
return true;
}
else
{
prcBar.Value = prcBar.Maximum;
this.Close();
return false;
}
}
return false;
}
接着就是主窗体了,如何进行操作了,首先需要定义两个私有成员,一个委托。其中一个私有成员是保存当前进度条窗体对象,另一个是保存委托方法(即增加进度条尺度),具体如下:
private frmProcessBar myProcessBar = null;
private delegate bool IncreaseHandle( int nValue );
private IncreaseHandle myIncrease = null;
接着要在主窗体中提供函数来打开进度条窗体,如下:
/// <summary>
/// Open process bar window
/// </summary>
private void ShowProcessBar()
{
myProcessBar = new frmProcessBar();
// Init increase event
myIncrease = new IncreaseHandle( myProcessBar.Increase );
myProcessBar.ShowDialog();
myProcessBar = null;
}
那么现在就可以开始创建线程来运行,具体如下:
/// <summary>
/// Sub thread function
/// </summary>
private void ThreadFun()
{
MethodInvoker mi = new MethodInvoker( ShowProcessBar );
this.BeginInvoke( mi );
Thread.Sleep( 1000 );//Sleep a while to show window
bool blnIncreased = false;
object objReturn = null;
do
{
Thread.Sleep( 50 );
objReturn = this.Invoke( this.myIncrease,
new object[]{ 2 } );
blnIncreased = (bool)objReturn ;
}
while( blnIncreased );
}
注意以上,在打开进度条窗体和增加进度条进度的时候,一个用的是BeginInvoke,一个是Invoke,这里的区别是BeginInvoke不需要等待方法运行完毕,而Invoke是要等待方法运行完毕。还有一点,此处用返回值来判断进度条是否到头了,如果需要有其他的控制,可以类似前面的方法来进行扩展。
启动线程,可以如下:
Thread thdSub = new Thread( new ThreadStart( ThreadFun ) );
thdSub.Start();
这样,一个用模式打开进度条窗体就做完了。
发表于 @ 2006年05月27日 10:43:00 | 评论( 55 ) | 编辑| 举报| 收藏
旧一篇:程序以及窗体运行的唯一性汇总 | 新一篇:如何去写一个.Net程序
- 查看最新精华文章 请访问博客首页相关文章
- 很久没发代码了,今天来发些C#代码
- 如何弹出一个模式窗口来显示进度条
- VS2005字符串资源不能识别转义字符
- 如何弹出一个模式窗口来显示进度条
- 如何弹出一个模式窗口来显示进度条
- 子线程控制主线程中UI显示
- c#模态进度条
- c#模态进度条
- Tuff 发表于2006年5月29日 22:11:00 IP:举报回复删除
- 未处理 System.InvalidOperationException
Message="在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。"
Source="System.Windows.Forms"
- knight94 发表于2006年5月30日 7:44:00 IP:举报回复删除
- to Tuff
出现这类问题,主要是窗体还没形成,就调用Invoke或者BeginInvoke方法。
哪个方法产生的异常,如果是第二个,你把Thread.Sleep时间加长一些,或者在其Form_load时间中尽快的显示出来,即用“this.Show()”。
- knight94 发表于2006年8月1日 17:49:00 IP:举报回复删除
- 参看我的另外一篇文章,关于线程类的,
地址:
http://blog.csdn.net/knight94/archive/2006/03/21/631238.aspx
- tianjj 发表于2006年8月15日 10:42:00 IP:举报回复删除
- this.BeginInvoke(mi);
==========
OK,
解决了,是.net1.1 和2.0下都调试成功。
如果在frmProcessBar做一个随这着进度条的百分比,是否还要有一个Invoke 线程。
- knight94 发表于2006年8月15日 12:01:00 IP:举报回复删除
- to tianjj
至于百分比之类的,属于细节问题,
我只是给了一个方法,怎么在我的基础上进行扩展属于你的工作。
我相信你会做得更漂亮。
- damofengbo 发表于2006年8月27日 16:37:00 IP:举报回复删除
- MethodInvoker mi = new MethodInvoker (ShowProcessBar );
this.BeginInvoke( mi );
出错:ShowProcessBar()
与委托“void System.Windows.Forms.MethodInvoker()”不匹配
- damofengbo 发表于2006年8月27日 16:32:00 IP:举报回复删除
- MethodInvoker mi = new MethodInvoker (ShowProcessBar );
this.BeginInvoke( mi );
出错:ShowProcessBar()
与委托“void System.Windows.Forms.MethodInvoker()”不匹配
- damofengbo 发表于2006年8月27日 17:41:00 IP:举报回复删除
- 问题:
主窗体类()
{控件1;A a;B b}
类A()
{B b; 方法F1(){b.F2(参数)}}
类B()
{控件1 方法F2(参数){访问控件1}}
线程函数里面调用a.F1()访问控件1,
这里好像是线程里面访问主界面的控件问题
请问我的方法F1里面该怎么写,
F1()
{
object[] pList = {参数};
MethodInvoker mi = new MethodInvoker(b.F2);
b.控件1.BeginInvoke(mi,pList);
}
- knight94 发表于2006年8月27日 18:39:00 IP:举报回复删除
- to damofengbo
MethodInvoker只是一个比较简单的委托,原型如下
public delegate void MethodInvoker();
如果你要使用MethodInvoker来定义对象,首先要看看是否符合上面的样式。
如果不符合的话,可以进行扩展,例如:
public delegate returntype myMethodInvoker( yourParam );
myMethodInvoker mi = new myMethodInvoker( .. );
yourControl.BeginInvoke( mi, new object[]{..} );
- damofengbo 发表于2006年8月28日 19:41:00 IP:举报回复删除
- 谢谢
- User 发表于2006年9月22日 11:51:00 IP:举报回复删除
- 学习:
我如果有如下:
// System.Data.SqlClient.SqlConnection m_conn = null;
// m_conn = new System.Data.SqlClient.SqlConnection();
// m_conn.ConnectionString = "Data Source=192.168.0.1; Initial Catalog=MaxData; User Id=sa; Password=123456;";
// m_conn.Open();
通过进度条来显示速度,该放到您的代码的什么位置呢?
- knight94 发表于2006年9月22日 12:13:00 IP:举报回复删除
- to user
对于你所说的,并不是很好控制,因为对于connection的open操作,你无法判断要用多少时间,因此进度条的信息只能是模拟,无法去精确控制。
线程中大致如下:
//打开进度条窗体
//用begininvoke去触发窗体来打开connection
while( !blnFinished )
{
//按照一定步频修改进度条
Thread.Sleep( 20 );
}
//关闭进度条窗体
以上是在线程函数中进行的操作。
那么对于连接可以如下:
private bool blnFinished = false;
private void ConnectDB()
{
//Open connection here
blnFinished = true;//Set signal here
}
- knight94 发表于2006年10月11日 18:44:00 IP:举报回复删除
- to User
如果是用DataAdapter.Fill的话,很难在进度条上体现;
如果是用DataReader来逐条读取的话,是可以的。
- yanam 发表于2006年10月13日 16:56:00 IP:举报回复删除
- IncreaseHandle myIncrease = new IncreaseHandle(myProcessBar.Increase);
为何我在这个地方会提示
实例方法的委托不能具有空“this”。
我用的是。net2.0
- knight94 发表于2006年10月14日 19:11:00 IP:举报回复删除
- to Jason_mf
我的例子中使用的Delegate是有返回值的,而返回值需要通过Object才能接收。
所以objReturn是接收委托函数返回值用的。
- zyc21st 发表于2006年10月22日 8:15:00 IP:举报回复删除
- 我将启动线程的语句
Thread thdSub = new Thread( new ThreadStart( ThreadFun ) );
thdSub.Start();
放在主窗体的一个方法体中执行,thdSub.Start();后面还有其他的语句,为什么必须该方法执行完后,进度条窗体才能启动?
不能执行完thdSub.Start();就启动进度条窗体?
- knight94 发表于2006年10月22日 9:49:00 IP:举报回复删除
- to zyc21st
你如果想在thdSub.Start()后立刻开启线程,可以在之后先加上
Thread.Sleep(20);//Run sub-thread
即可。
- zyc21st 发表于2006年10月22日 10:53:00 IP:举报回复删除
- 我现在想实现这样一个效果:
在主窗口中点击按钮Download一个文件,并打开进度条等待
private void button1_Click(object sender, System.EventArgs e)
{
Thread thdSub = new Thread( new ThreadStart( ThreadFun ) );
thdSub.Start();
Thread.Sleep(20);
ftp.Download();
}
现在好像必须ftp.Download();执行完成之后,进度窗体才能显示,我现在想thdSub.Start();之后进度窗体显示,进度条开始滚动,同时主窗体在执行ftp.Download();下载文件,这个地方应该怎么写?谢谢你了
- knight94 发表于2006年10月31日 12:54:00 IP:举报回复删除
- to webber
你如何写的,
这里需要注意两点,
1、如果窗体用ShowDialog打开,不要用Invoke,而要用BeginInvoke;
2、如果窗体用Show打开,不要把窗体定义为局部变量,把它改成成员。
- webber 发表于2006年10月31日 11:48:00 IP:举报回复删除
- 我在调用ShowProcessBar()的时候用的是Invoke方法(自己写了一个委托),但是当弹出进度条窗体的时候就没有反应了,因为什么啊?
- knight94 发表于2006年11月1日 10:22:00 IP:举报回复删除
- to yanam
仔细检查你的程序,以我的程序进行参考,对比细节处,是否处理正确。
to webber
不一定是MethodInvoker,Invoke或者BeginInvoke只是执行一个委托函数而已,并不要求委托函数类型具体是什么,这方面你可以参看
http://blog.csdn.net/Knight94/archive/2006/08/24/1111267.aspx
- yanam 发表于2006年11月2日 15:04:00 IP:举报回复删除
- knight94 你好,这个进度条我是这样调用的
private void ShareSelect_Load(object sender, EventArgs e)
{
Thread thdSub = new Thread(new ThreadStart(ThreadFun));
thdSub.Start();
initData();//填充datagrid
}
如果用线程调用initdata,会出错,如何才能initdata完成就进度条完成100关闭呢?
谢谢,刚学c#,很多东西不熟悉
- knight94 发表于2006年11月2日 17:05:00 IP:举报回复删除
- to yanam
如果比较正确地完成你的功能,需要把initData这个函数放到线程中去做,并写在初始化datagrid时候,动态改变进度条的值。
那么其中涉及到的invoke或者begininvoke有三个方面
1、进度条的窗体模态显示;
2、进度条的变动;
3、datagrid的操作。
那么这些都可以参照我给的方法去完成即可。
- vessel 发表于2006年11月13日 11:11:00 IP:举报回复删除
- knight94
你好,我想实现调用一个dll函数,这个函数调用会占比较长的时间,所以显示进度条。
希望效果:
开始调用该函数时显示进度条窗体,并不断让进度条滚动,函数调用好后关闭进度条窗体,由于不能确定函数调用时间,把进度条style设成Marquee就可以了。
具体该怎么做?谢谢!
- knight94 发表于2006年11月13日 15:26:00 IP:举报回复删除
- to vessel
文章控制进度条的显示是在“ThreadFun”这个函数中,那么你可以对它进行扩展,让调用函数和进度条的显示进行同步。
- zhouxiaoming 发表于2008年3月14日 15:33:04 IP:举报回复删除
- hi,你好
我还是没搞好,请帮我看看啊。
具体如下:
一个费时的操作A,在执行的时候会自动弹出几个窗体;
一个记录操作A进度的全局变量i,用来显示执行进度;
method A()
{
...
Thread thdSub = new Thread(new ThreadStart(ThreadFun));
thdSub.Start();
...(省略的用method B表示吧)
}
method B()
{
...
i++;
...
}
//显示进度
private void ThreadFun()
{
MethodInvoker mi = new MethodInvoker(ShowProcessBar);
this.BeginInvoke(mi);
//Thread.Sleep( 500 );//Sleep a while to show window
bool blnIncreased = false;
object objReturn = null;
do
{
Thread.Sleep( 50 );
objReturn = this.Invoke( this.myIncrease,
new object[] { methodC(i) });//methodC()为关于i的的函数,用来显示进度
blnIncreased = (bool)objReturn ;
}
while( blnIncreased );
}
正如你所说,Invoke调用要等待方法A结束才开始,也就是方法B结束,这样ThreadFun没有执行也就不能实时的反映进度了,请问怎样解决。
谢谢!
- zhouxiaoming 发表于2008年3月14日 15:36:33 IP:举报回复删除
- 我刚刚发了一遍了啊,呵呵。也就如何同步的问题?我把Invoke改成BeginInvoke就有错误发生,真的不知道怎么弄了。
拜谢解答如何同步!!!
- gengxin_914 发表于2008年7月7日 21:43:44 IP:举报回复删除
- 如果想用代码来关闭该等待窗口该怎么实现啊,比如:
Thread thdSub = new Thread( new ThreadStart( ThreadFun ) );
thdSub.Start();
//执行长时间等待的函数,执行时间不确定
//函数执行完毕后自动关闭等待窗口
- gengxin_914 发表于2008年7月14日 15:22:27 IP:举报回复删除
- 如果我想通过代码提前将等待窗口关闭,那用代码该怎么写啊?
- zhenqiyi 发表于2008年7月23日 23:00:27 IP:举报回复删除
- 我是个菜鸟,第一次做DLL,我是做了一个带界面的DLL,ASP.NET里调用它的,就是说在网页里点下按钮打开DLL里的那个页面,在这个页面里有一个打开文件的按钮,按这个就会报错:在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。
具体线程什么的我不懂啊,代码是这样的,大师有空帮我看看哈:
在ASP.NET里:
protected void OKADD_Click(object sender, EventArgs e)
{//testExcelToOracle是我的DLL的NAMESPACE
testExcelToOracle.Form1 TESTadd = new testExcelToOracle.Form1 ();
TESTadd.ShowDialog ();
}
DLL的FORM1里:
private void openExcel_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "c:\\";//注意这里写路径时要用c:\\而不是c:\
// openFileDialog.Filter="文本文件|*.*|Excel文件|*.xls|C#文件|*.cs|所有文件|*.*";
openFileDialog.Filter = "Excel文件|*.xls";
openFileDialog.RestoreDirectory = true;
openFileDialog.Title = "打开文件";
openFileDialog.FilterIndex = 1;
//在这里报错的。。
if (openFileDialog.ShowDialog() == DialogResult.OK )
。。。。。。
}
万分感谢!!!
- zhenqiyi 发表于2008年7月23日 23:00:40 IP:举报回复删除
- 我是个菜鸟,第一次做DLL,我是做了一个带界面的DLL,ASP.NET里调用它的,就是说在网页里点下按钮打开DLL里的那个页面,在这个页面里有一个打开文件的按钮,按这个就会报错:在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。
具体线程什么的我不懂啊,代码是这样的,大师有空帮我看看哈:
在ASP.NET里:
protected void OKADD_Click(object sender, EventArgs e)
{//testExcelToOracle是我的DLL的NAMESPACE
testExcelToOracle.Form1 TESTadd = new testExcelToOracle.Form1 ();
TESTadd.ShowDialog ();
}
DLL的FORM1里:
private void openExcel_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "c:\\";//注意这里写路径时要用c:\\而不是c:\
// openFileDialog.Filter="文本文件|*.*|Excel文件|*.xls|C#文件|*.cs|所有文件|*.*";
openFileDialog.Filter = "Excel文件|*.xls";
openFileDialog.RestoreDirectory = true;
openFileDialog.Title = "打开文件";
openFileDialog.FilterIndex = 1;
//在这里报错的。。
if (openFileDialog.ShowDialog() == DialogResult.OK )
。。。。。。
}
万分感谢!!!
- tiandaiyin 发表于2010年8月24日 12:20:09 IP:124.205.192.*举报回复删除
- 代码给全好吧,还有就是注释能不能用国语,俺是中国人,希望能看自己国家的语言!
- leonardo_ding 发表于2010年9月28日 13:23:28 IP:218.94.124.*举报回复删除
knight94 你好!
- leonardo_ding 发表于2010年9月28日 13:24:03 IP:218.94.124.*举报回复删除
- 向你请教一个问题!
- leonardo_ding 发表于2010年9月28日 13:28:31 IP:218.94.124.*举报回复删除
- private void cccc_ItemClick(object sender, ItemClickEventArgs e) { thdSub = new Thread(new ThreadStart(ThreadFun)); thdSub.Start(); } private void ThreadFun() { 任务1 任务2 任务3 任务4 } 这四个任务都是用委托写的 this.Invoke()的形式我不知道如何写个进度条,让进度条从逐格推进,直到满格,任务也处理完成???谢谢指导
-
- 发表评论
-
- 评论内容:
- 验 证 码:
重新获得验证码
-
热门招聘职位
- 【京东商城】诚聘手机开发工程师,软件开发工程师,欲试从速!
- 【Autodesk】欧特克软件(中国)诚聘软件开发,测试,研究员
- 【东星软件(上海)有限公司】诚聘Flash程序员、3D游戏引擎研发工程师、手机软件市场运营
- 【深圳好伴电子商务】高薪诚聘:PHP、网络前端工程师、网页设计工程师!
- 【Google】诚招技术精英,史上人数最多职位最广!
- 【敦煌网】诚聘研发类职位:Java、PHP、网站架构师、运维工程师 等职位
- 【柯达(Kodak)】 诚聘C++(视频图像处理)/C#/Java Engineer,(工作地点:上海)
- 【新太科技】高薪诚聘各类软件开发人才(工作地点:广州,北京)
- 【热聘】搜狐畅游全国热招开发工程师
- 【网易杭州】技术类职位大招聘:c++、java、信息安全工程师等职位热招中
- 【百度】诚聘 Web研发/工程师 一个舞台,让你的想法去成为现实!!!
- 【上海我友】福利购房计划+高薪+期权,邀您共创互联网的奇迹!
- 【京东商城】诚聘手机开发工程师,软件开发工程师,欲试从速!
- 【Autodesk】欧特克软件(中国)诚聘软件开发,测试,研究员
- 【东星软件(上海)有限公司】诚聘Flash程序员、3D游戏引擎研发工程师、手机软件市场运营
- 【深圳好伴电子商务】高薪诚聘:PHP、网络前端工程师、网页设计工程师!
- 【Google】诚招技术精英,史上人数最多职位最广!
- 【敦煌网】诚聘研发类职位:Java、PHP、网站架构师、运维工程师 等职位
- 【柯达(Kodak)】 诚聘C++(视频图像处理)/C#/Java Engineer,(工作地点:上海)
- 【新太科技】高薪诚聘各类软件开发人才(工作地点:广州,北京)
- 【热聘】搜狐畅游全国热招开发工程师
- 【网易杭州】技术类职位大招聘:c++、java、信息安全工程师等职位热招中
- 【百度】诚聘 Web研发/工程师 一个舞台,让你的想法去成为现实!!!
- 【上海我友】福利购房计划+高薪+期权,邀您共创互联网的奇迹!
- 公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告
- 北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号
- 世纪乐知(北京)网络技术有限公司 提供技术支持
- 江苏乐知网络技术有限公司 提供商务支持
Email:webmaster@csdn.net - Copyright © 1999-2010, CSDN.NET, All Rights Reserved
![GongshangLogo]()
最近看了好多人问这方面的问题,以前我也写过一篇blog,里面说了如何在子线程中控制进度条。但目前大多数环境,需要弹出模式窗口,来显示进度条,那么只需要在原先的基础上稍作修改即可。
首先是进度条窗体,需要在上面添加进度条,然后去掉ControlBox。除此外,还要增加一个方法,用来控制进度条的增加幅度,具体如下:
/// <summary>
/// Increase process bar
/// </summary>
/// <param name="nValue">the value increased</param>
/// <returns></returns>
public bool Increase( int nValue )
{
if( nValue > 0 )
{
if( prcBar.Value + nValue < prcBar.Maximum )
{
prcBar.Value += nValue;
return true;
}
else
{
prcBar.Value = prcBar.Maximum;
this.Close();
return false;
}
}
return false;
}
接着就是主窗体了,如何进行操作了,首先需要定义两个私有成员,一个委托。其中一个私有成员是保存当前进度条窗体对象,另一个是保存委托方法(即增加进度条尺度),具体如下:
private frmProcessBar myProcessBar = null;
private delegate bool IncreaseHandle( int nValue );
private IncreaseHandle myIncrease = null;
接着要在主窗体中提供函数来打开进度条窗体,如下:
/// <summary>
/// Open process bar window
/// </summary>
private void ShowProcessBar()
{
myProcessBar = new frmProcessBar();
// Init increase event
myIncrease = new IncreaseHandle( myProcessBar.Increase );
myProcessBar.ShowDialog();
myProcessBar = null;
}
那么现在就可以开始创建线程来运行,具体如下:
/// <summary>
/// Sub thread function
/// </summary>
private void ThreadFun()
{
MethodInvoker mi = new MethodInvoker( ShowProcessBar );
this.BeginInvoke( mi );
Thread.Sleep( 1000 );//Sleep a while to show window
bool blnIncreased = false;
object objReturn = null;
do
{
Thread.Sleep( 50 );
objReturn = this.Invoke( this.myIncrease,
new object[]{ 2 } );
blnIncreased = (bool)objReturn ;
}
while( blnIncreased );
}
注意以上,在打开进度条窗体和增加进度条进度的时候,一个用的是BeginInvoke,一个是Invoke,这里的区别是BeginInvoke不需要等待方法运行完毕,而Invoke是要等待方法运行完毕。还有一点,此处用返回值来判断进度条是否到头了,如果需要有其他的控制,可以类似前面的方法来进行扩展。
启动线程,可以如下:
Thread thdSub = new Thread( new ThreadStart( ThreadFun ) );
thdSub.Start();
这样,一个用模式打开进度条窗体就做完了。
knight94 你好!








Email:webmaster@csdn.net 
浙公网安备 33010602011771号