c#多线程问题(.net 2010)
我用的是.net 2010的C#,winform.目标是在窗体上按下按钮,3条线程同时对三个文件进行信息采集。当3个进程全部结束后再进行信息整合。
我尝试了Threadool但是在waithandle.waitall的时候得到了"wait for multiple handles on a sta thread is not supported"的错误。然后才知道从窗体开始执行的thread都是STA。
如果是这样的话,如何才能得到我想要的结果呢?
我刚开始学习,不是很理解,还望大家说得详细点
谢谢 --------------------编程问答-------------------- 是不是你调用了公寓多线程的ActiveX COM DLL?
在 void Main() 前加上[MTAThread]看看。 --------------------编程问答-------------------- use Thread.Joing() to wait other thread finish their jobs. --------------------编程问答-------------------- Thread.Join(); --------------------编程问答-------------------- 三个采集线程结束先后顺序是不一定的,使用主线程上waitone()之类的恐怕有些麻烦,倒不如在窗体类设三个判断变量,每个线程上的启动是由委托启动(或者直接使用BackGroundWorker),执行完就回调,回调函数设置对应线程结束的判断变量,并确定所有判断变量都设置完成了,即可发起后续作业,这样会好些。
delegate_xxA collectA=....;
delegate_xxB collectB=...;
delegate_xxC collectC=...;
collectA.BeginInvoke(。。。);
collectB.BeginInvoke(。。。);
collectC.BeginInvoke(。。。);
--------------------编程问答--------------------
靠谱 --------------------编程问答-------------------- 如果使用异步委托的话,有个例程给你看看,但理解起来很麻烦,你最好直接使用BackgroundWorker这个控件,(在控件工具箱里面有),设置看看说明很快就会了。
--------------------编程问答--------------------
#region 异步回调
private void button5_Click(object sender, EventArgs e)
{
EventHandler p = new EventHandler(button2);
p.BeginInvoke(sender, e, new AsyncCallback(CallBackMth), null);
}
void CallBackMth(IAsyncResult target)
{
//后续操作....
}
#endregion
你说得这个方法管用 --------------------编程问答--------------------
我用backgroundworker的话怎么做能够等到三个进程都结束了才开始信息的合成,我这样写的又不行
private void button3_Click(object sender, EventArgs e)
{
bgw1.RunWorkerAsync();
Thread.Sleep(10);
bgw2.RunWorkerAsync();
Thread.Sleep(10);
bgw3.RunWorkerAsync();
Thread.Sleep(10);
while (bgw1.IsBusy && bgw2.IsBusy && bgw3.IsBusy)
{
// Console.WriteLine ("hello");
}
MessageBox.Show("all finished"); //好像进了死循环,一直看不到这里。
}
给点提示吧,再次感谢 --------------------编程问答-------------------- 我改了一下就可以了
while (bgw1.IsBusy && bgw2.IsBusy && bgw3.IsBusy)
{
// Console.WriteLine ("hello");
application.doevents();
}
MessageBox.Show("all finished");
不过这样是不是不好呢?还是这样是正常的方法?求解惑,谢谢 --------------------编程问答-------------------- [Quote=引用 8 楼 clear_zero 的回复:]
我用backgroundworker的话怎么做能够等到三个进程都结束了才开始信息的合成,我这样写的又不行
private void button3_Click(object sender, EventArgs e)
{
bgw1.RunWorkerAsync();
Thread.Sleep(10);
bgw2.RunWorkerAsync();
Thread.Sleep(10);
bgw3.RunWorkerAsync();
Thread.Sleep(10);
while (bgw1.IsBusy && bgw2.IsBusy && bgw3.IsBusy)
{
// Console.WriteLine ("hello");
}
MessageBox.Show("all finished"); //好像进了死循环,一直看不到这里。
}
给点提示吧,再次感谢
Quote]
这样编码效率并不高,并没有脱离同步编程的思考模式,异步编程需要改变一下思路:即线程执行完后,系统会自动调用回调函数,后续的执行不是在主线程等待,而是在回调函数中处理。
即对backgroundWorker注册RunWorkerCompleted事件,在事件中判断是否所有收集工作都完成了。
#if 方式1
ManuResetEvent _CEvent= new ...;
#endif
bool _IsBgw1Complete;
bool _IsBgw2Complete;
bool _IsBgw3Complete;
private void button3_Click(object sender, EventArgs e)
{
#if 方式1
CEvent.Reset();
#endif
_IsBgw1Complete=false;
_IsBgw2Complete=false;
_IsBgw3Complete=false;
bgw1.RunWorkerAsync();
bgw2.RunWorkerAsync();
bgw3.RunWorkerAsync();
#if 方式1
//方式1:系统需要等待所有采集线程完成,则可设置一个ManuResetEvent,在
if( _CEvent.WaitOne(可以设置等待的超时))
{
DoAfterCollect()
}
else
{
//超时失败的处理
}
#else
//方式2: 不阻塞,直接关闭,界面可以直接处理其他事务,而在所有采集工作完成后,自动弹出后续操作需要的窗体。因此直接结束即可
#endif
}
private void bgw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_IsBgw1Complete=true;
if (_IsBgw1Complete && _IsBgw2Complete&& _IsBgw3Complete)
{
#if 方式1
_CEvent.Set();
#else
//方式2,
DoAfterCollect();
#endif
}
}
private void DoAfterCollect()
{
// Console.WriteLine ("hello");
}
使用主线程waitone()一类的方法,主线程一直被阻塞,会占用2MB的线程栈内存空间,放到界面则用户只能等待。而使用异步线程结束后回调处理,内存空间比较省。用户也可以进行其他操作(当然代价是速度比阻塞有极小的慢,慢的可以忽略)。
--------------------编程问答-------------------- Thread.Join(); --------------------编程问答-------------------- 另外还可以辅助的增加一个进度条,每完成一项工作,向进度条发送一个信号,用户还可以选择在未完成全部工作时取消作业,随时退出, --------------------编程问答--------------------
方法2成功了,但是1不成功不知道为什么。
我已经添加了进度条,方法2的时候更新进度条。处理DoAfterCollect都没有问题。我想用方法1是因为方法1还是归总到主进程来。而方法2会在三个地方触发虽然可以通过变量控制但是感觉不是很好。方法1也不更新进度条
//声明的部分
private ManualResetEvent m_ResetEvent = new ManualResetEvent(false);
private bool m_bgw1Finish=false;
private bool m_bgw2Finish=false ;
private bool m_bgw3Finish=false ;
....
....
//开始进程的代码
private void button3_Click(object sender, EventArgs e)
{
m_ResetEvent.Reset();
m_bgw1Finish = false;
m_bgw2Finish = false;
m_bgw3Finish = false;
bgw1.RunWorkerAsync();
Thread.Sleep(10);
bgw2.RunWorkerAsync();
Thread.Sleep(10);
bgw3.RunWorkerAsync();
Thread.Sleep(10);
//MessageBox.Show("all finished");
if (m_ResetEvent.WaitOne( ))
{
MessageBox.Show("all finished");
}
}
//我在每个进程结束的事件里面都写了相应的代码
private void bgw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
m_bgw1Finish = true;
if (m_bgw1Finish || m_bgw2Finish || m_bgw3Finish)
{
// MessageBox.Show("all finished");
m_ResetEvent.Set() ;
}
}
我哪里写错了么?求指导
谢谢
--------------------编程问答-------------------- 对不起,上面的判断||应该改成&&
不过方法1还是没有反应
谢谢 --------------------编程问答-------------------- 路過學習了。 --------------------编程问答-------------------- 建议通过静态变量来标记完成的线程数,同时利用一个处理的标记来标记是否已经对线程的结果进行了处理,并通过异步调用的方式使用多线程处理。具体代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace ConsoleApplication1
{
class Program
{
public static int ThreadCount = 0;//用于标记已经结束处理的线程数
public static int FinishedProcess = 0;//用于标记是否进行了线程处理整合的标记,0代表未处理,1代表已经处理了
static void Main(string[] args)
{
//定义三个委托并指向不同的方法
Func<int, string> workerMethod1 = Method1;
Func<int, string> workerMethod2 = Method2;
Func<int, string> workerMethod3 = Method3;
//进行委托的异步调用,参数1只是作为一个指示,没有什么实际意义,回调函数都定义为CallBack
IAsyncResult asyncResult1 = workerMethod1.BeginInvoke(1, new AsyncCallback(CallBack), null);
IAsyncResult asyncResult2 = workerMethod2.BeginInvoke(1, new AsyncCallback(CallBack), null);
IAsyncResult asyncResult3 = workerMethod3.BeginInvoke(1, new AsyncCallback(CallBack), null);
//防止程序运行则终止
Console.ReadKey();
}
public static void CallBack(IAsyncResult ar)
{
Console.WriteLine("当前线程为:"+Thread.CurrentThread.ManagedThreadId + "ThreadCount值为:" + ThreadCount);
Func<int, string> proc=((AsyncResult)ar).AsyncDelegate as Func<int, string>;
string c = proc.EndInvoke(ar);
if (c == "3"&&FinishedProcess==0)
{
FinishedProcess = 1;
Console.WriteLine("所有线程已经处理完毕,请在此处进行最后的整合");
}
}
public static string Method1(int i)
{
ThreadCount++;
Console.WriteLine("方法:Method1;当前线程为:" + Thread.CurrentThread.ManagedThreadId+";ThreadCount值为:"+ThreadCount);
return ThreadCount.ToString();
}
public static string Method2(int i)
{
ThreadCount++;
Console.WriteLine("方法:Method2;当前线程为:" + Thread.CurrentThread.ManagedThreadId + ";ThreadCount值为:" + ThreadCount);
return ThreadCount.ToString();
}
public static string Method3(int i)
{
ThreadCount++;
Console.WriteLine("方法:Method3;当前线程为:" + Thread.CurrentThread.ManagedThreadId + ";ThreadCount值为:" + ThreadCount);
return ThreadCount.ToString();
}
}
}
上面代码中在每个线程处理完毕的时候都对TreadCount这个变量加1,同时在回调函数CallBack中判断当前完成的线程数是否为3,若是三则进行必要的处理,同时对处理标记FinishedProcess置为1,这样保证只对结果进行一次处理。
运行结果不确定,在多核机器中,基本上是同步完成的。
在实际操作中应该把ThreadCount++放在每个处理逻辑的最后,保证是处理线程的最后一个工作。这样就能保证当ThreadCount为3的时候标识3个线程都处理完毕了。 --------------------编程问答--------------------
1. 多线程操作需要考虑很多线程间同步的问题,需要考虑可能多个线程同时访问某段代码/某个变量带来的冲突问题,会频繁的使用lock命令,但lock不能锁int,bool等栈内的变量。
比如,如果我们担心两个采集线程同时执行造成的混乱,可以:
object _ForLockObj=new object();
....
lock(_ForLockObj)
{
if (m_bgw1Finish || m_bgw2Finish || m_bgw3Finish)
{
// MessageBox.Show("all finished");
m_ResetEvent.Set() ;
}
}
2. 对于方法1没有调通的问题,其实比较好调,
1)对 m_ResetEvent.waitone()设置超时,看看是否为超时未等到,如果是,则为多线程内逻辑问题。
2)如果未等到,还可以观察各个判断变量那个发生变化了,如果都没变化,看看 m_ResetEvent是否reset(). --------------------编程问答-------------------- 另外,方法一也可以更新进度条。 --------------------编程问答--------------------
调了,没有进入任何一个线程的
private void bgw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
m_bgw1Finish = true;
if (m_bgw1Finish && m_bgw2Finish && m_bgw3Finish)
{
// MessageBox.Show("all finished");
m_ResetEvent.Set() ;
}
}
我回头再弄弄,搞不定阿...头疼中 --------------------编程问答-------------------- 给你揉揉头,挤挤太阳穴,我刚接触时也有时范迷瞪,硬着头皮上着做几个案例就通了。
不过有C++底层编程的底子好些(那里有个互斥锁的概念),C#的线程需要占用线程栈,如果做服务器应用就得注意内存的使用,如果是界面应用就无所谓了。 --------------------编程问答-------------------- 另外,异步编程的调试麻烦些,有时捕获不到中断,有时两个中断一起走很乱,这是最好把过程变量或异常抛出到调试文件或系统日志中。 --------------------编程问答--------------------
我理解这个互斥锁的概念。用你的方法2也实现了。只是这方法一并没有实现。我回头需要重新实践一下threadpool的东西.好像这个invoke我并没有搞明白
...笨死算了
补充:.NET技术 , C#