由于winform的缘故,最近一周都在研究多线程,以前也看过多线程方面的资料,一个WaitOne就把我难倒了。呵呵,现在总算搞清楚了。
本片(参考自codeproject)主要讲以下内容:
(一).Sleep和Join的用法
(二).Invoke和BeginInvoke的使用及区别
(三).Monitor类的使用
(四).AutoResetEvent使用
(一)1.Sleep()方法,调用后,线程会被阻止指定的时间,时间到了后,会继续执行。
线程需引用using System.Threading;命名空间。
Code
class Sleep //Thread.Sleep的用法
{
public static Thread T1;
public static Thread T2;
public static void Main(string[] args)
{
Console.WriteLine("Enter Main method");
T1 = new Thread(new ThreadStart(Count1));
T2 = new Thread(new ThreadStart(Count2));
T1.Start();
T2.Start();
Console.WriteLine("Exit Main method");
Console.ReadLine();
}
//thread T1 threadStart
private static void Count1()
{
Console.WriteLine("Enter T1 counter");
for (int i = 0; i < 50; i++)
{
Console.Write(i + " ");
if (i == 10)
{
Thread.Sleep(1000);
}
}
Console.WriteLine("Exit T1 counter");
}
//thread T2 threadStart
private static void Count2()
{
Console.WriteLine("Enter T2 counter");
for (int i = 51; i < 100; i++)
{
Console.Write(i + " ");
if (i == 70)
{
Thread.Sleep(5000);
}
}
Console.WriteLine("Exit T2 counter");
}
}
2.Join()方法,肯定有两个线程,Join才有用。
比如两个线程T1,T2
你在T2里写上T1.Join()的话,在执行的时候,T2会被Block,直到T1是的状态是stopped。
Code
class Program //Join的用法
{
public static Thread T1;
public static Thread T2;
static void Main(string[] args)
{
T1 = new Thread(new ThreadStart(First));
T2 = new Thread(new ThreadStart(Second));
T1.Name = "T1";
T2.Name = "T2";
T1.Start();
T2.Start();
Console.ReadLine();
}
//thread T1 threadStart
private static void First()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("T1 state [{0}], T1 showing {1}", T1.ThreadState, i.ToString());
}
}
//thread T2 threadStart
private static void Second()
{
//what the state of both threads
Console.WriteLine("T2 state [{0}] just about to Join, T1 state [{1}], CurrentThreadName={2}", T2.ThreadState, T1.ThreadState, Thread.CurrentThread.Name);
//join T1
T1.Join(); //T2直到T1的状态是Stopped时,才开始执行
Console.WriteLine("T2 state [{0}] T2 just joined T1, T1 state [{1}], CurrentThreadName={2}", T2.ThreadState, T1.ThreadState, Thread.CurrentThread.Name);
for (int i = 5; i < 10; i++)
{
Console.WriteLine("T2 state [{0}], T1 state [{1}], CurrentThreadName={2} showing {3}", T2.ThreadState, T1.ThreadState, Thread.CurrentThread.Name, i.ToString());
}
Console.WriteLine("T2 state [{0}], T1 state [{1}], CurrentThreadName={2}", T2.ThreadState, T1.ThreadState, Thread.CurrentThread.Name);
}
(二).Invoke和BeginInvoke
Code
1. class Program
{
static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(Program.MyFunction));
t.Start();
for (int i = 0; i < 100; i++)
{
Console.WriteLine("I am in Main Thread {0}", i);
}
Console.Read();
}
public static void MyFunction()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("I am in Different Thread {0}", i);
}
}
}
由于程序的并行执行,所以会交替输出。
Code
2. class Program1
{
public delegate void MyDelegate(int i, string str);
static void Main(string[] args)
{
MyDelegate delInstance = new MyDelegate(MyFunction);
delInstance.BeginInvoke(100, "I am in Delegate Thread", null, null);
//delInstance.Invoke(100, "I am in Delegate Thread");
for (int i = 0; i < 100; i++)
{
Console.WriteLine("I am in Main Thread {0}", i);
}
Console.Read();
}
public static void MyFunction(int count, string str)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(str + " {0}", i);
}
}
}
这个输出和上面输出差不多,也是交替输出的。
Code
3. class Program2
{
public delegate int MyDelegate(int i, string str);
static void Main(string[] args)
{
MyDelegate delInstance = new MyDelegate(MyFunction);
IAsyncResult res = delInstance.BeginInvoke(100, "I am in Delegate Thread", null, null);
for (int i = 0; i < 100; i++)
{
Console.WriteLine("I am in Main Thread {0}", i);
}
int status = delInstance.EndInvoke(res);
Console.WriteLine(status);
Console.Read();
}
public static int MyFunction(int count, string str)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(str + " {0}", i);
}
return 999;
}
}
BeginInvoke()的返回值是IAsyncResult类型的,表示dddddddddddddddd
EndInvoke()可以获得返回值。
Code
4. class Program3
{
public delegate void MyDelegate(int i, string str);
static void Main(string[] args)
{
MyDelegate delInstance = new MyDelegate(MyFunction);
IAsyncResult res = delInstance.BeginInvoke(100, "I am in Delegate Thread", null, null);
delInstance.EndInvoke(res); //EndInvoke方法是获得线程执行代码的返回值用的,它会block当前线程,直到此线程执行完成
for (int i = 0; i < 100; i++)
{
Console.WriteLine("I am in Main Thread {0}", i);
}
Console.Read();
}
public static void MyFunction(int count, string str)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(str + " {0}", i);
}
}
}
Winform中的多线程与UI
Code
5. private void Form1_Load(object sender, EventArgs e)
{
AddControl();
}
void AddControl()
{
TextBox textBox1 = new TextBox();
Controls.Add(textBox1);
}
大家看到上面这段代码没任何问题,就是往窗体上面添加个TextBox控件。
Code
6.private void Form2_Load(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(RunInThread));
t.Start();
}
void RunInThread()
{
AddControl();
}
void AddControl()
{
TextBox textBox1 = new TextBox();
Controls.Add(textBox1);
}
大家再看这段代码,貌似也没有问题,但是运行的时候,就会抛出异常。
线程间操作无效: 从不是创建控件“Form2”的线程访问它。
出错的原因在于,主线程创建了form窗体,别的线程是不能操作它的。
Code
7. public delegate void MyDelegate();
private void Form3_Load(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(RunInThread));
t.Start();
}
void RunInThread()
{
MyDelegate delInstance = new MyDelegate(AddControl);
this.Invoke(delInstance);
MessageBox.Show("Hello");
//注释的地方,就是这里可以写上需要别的线程执行的代码,除了更新UI
}
void AddControl()
{
TextBox textBox1 = new TextBox();
Controls.Add(textBox1);
}
此处首先声明了委托MyDelegate。
this.Invoke(delInstance); 中的this代表的是form对象,实际上还是主线程执行的AddControl
Invoke方法,会阻塞别的线程,直到自己的调用返回了,才继续往下执行,就是说Invoke()调用后会立即返回。
BeginInvoke就不会阻塞别的线程。这是二者的区别之一。
Code
8.Form_Load(……)
{
MyDelagate delInstatnce = new MyDelagate(AddControl);
this.Invoke(delInstatnce);
}
void AddControl()
{
TextBox textBox1 = new TextBox();
Controls.add(textBox1);
}
上面的代码中,我们没必要用Invoke方法,因为我们是在主线程中,主线程中可以直接更新UI的。但是在一些情况下,我们不知道是否个别方法是在主线程里还是别的线程里执行的,并且,调用者不知道是否个别方法有更新UI的代码。所以,如果更新UI的话,应该切换到主线程里去执行。
其实没那么麻烦,InvokeRequired属性就是为了这个目的,如果当前线程不是主线程,InvokeRequired就返回true,是主线程就返回false。因此,任何更新UI的代码应该先判断InvokeRequired。
Code
Form_Load(……) //this function runs in main thread.
{
AddControl();
}
void RunInThread() //this function runs in a different thread.
{
AddControl();
}
void AddControl()
{
if(this.InvokeRequired)
{
MyDelagate delInstatnce = new MyDelagate(AddControl);
this.Invoke(delInstatnce);
return;
}
TextBox textBox1 = new TextBox();
Controls.add(textBox1);
}
(三).Monitor.Wait()方法,msdn解释:当前拥有指定对象上的锁的线程调用此方法以释放该对象,以便其他线程可以访问它。调用方在等待重新获取锁期间被阻止。当调用方需要等待另一个线程操作导致的状态更改时,将调用此方法。
当线程调用 Wait 时,它释放对象的锁并进入对象的等待队列。对象的就绪队列中的下一个线程(如果有)获取锁并拥有对对象的独占使用。所有调用 Wait 的线程都将留在等待队列中,直到它们接收到由锁的所有者发送的 Pulse 或 PulseAll 的信号为止。如果发送了 Pulse,则只影响位于等待队列最前面的线程。如果发送了 PulseAll,则将影响正等待该对象的所有线程。接收到信号后,一个或多个线程将离开等待队列而进入就绪队列。就绪队列中的线程被允许重新获取锁。
当调用线程重新获取对象上的锁后,此方法将返回。请注意,如果锁的持有者不调用 Pulse 或 PulseAll,则此方法将无限期地阻止。
调用方执行一次 Wait,与已为指定对象调用 Enter 的次数无关。概念上,Wait 方法存储调用方对对象调用 Enter 的次数,并按完全释放锁定对象所需要的次数调用 Exit。然后调用方在等待重新获取对象期间被阻止。当调用方重新获取锁时,系统按还原调用方的已保存 Enter 计数所需要的次数调用 Enter。调用 Wait 仅释放指定对象的锁;如果调用方是其他对象的锁的所有者,则不释放这些锁。
(四)
Code
1.using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace CodeProjectThread
{
class Program4
{
AutoResetEvent m_are;
static void Main(string[] args)
{
Program4 p = new Program4();
Console.Read();
}
Program4()
{
m_are = new AutoResetEvent(false); //false非终止,非终止状态不允许线程继续,true终止,终止状态允许线程继续
Thread t1 = new Thread(new ThreadStart(abc));
Thread t2 = new Thread(new ThreadStart(xyz));
t1.Start();
t2.Start();
int i = 0;
while (true)
{
Console.WriteLine("t1:" + t1.ThreadState + " " + "t2:" + t2.ThreadState + " " + i);
Thread.Sleep(2000);
if (i == 5)
{
m_are.Set();//允许一个等待线程继续
}
if (i == 10)
{
m_are.Set();//允许一个等待线程继续
}
if (i == 20) //中断while循环
{
break;
}
i++;
}
Console.Read();
}
void abc()
{
m_are.WaitOne(); //阻止当前线程
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000);
Console.WriteLine("abc abc abc");
}
}
void xyz()
{
m_are.WaitOne(); //阻止当前线程
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000);
Console.WriteLine("xyz xyz xyz");
}
}
}
}
Code
2.using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace CodeProjectThread
{
class Program5
{
public static Thread T1;
public static Thread T2;
public static AutoResetEvent ar1 = new AutoResetEvent(false); //非终止状态
public static AutoResetEvent ar2 = new AutoResetEvent(true); //终止状态
static void Main(string[] args)
{
T1 = new Thread((ThreadStart)delegate
{
Console.WriteLine("T1 is simulating some work by sleeping for 5 secs");
Thread.Sleep(5000);
Console.WriteLine("T1 is just about to set AutoResetEvent ar1");
ar1.Set(); //将事件状态设置为终止状态,允许等待线程继续
});
T2 = new Thread((ThreadStart)delegate
{
Console.WriteLine("T2 starting to wait for AutoResetEvent ar1,at time {0}", DateTime.Now.ToLongTimeString());
ar1.WaitOne(); //阻止当前线程,直到WaitHandle收到信号
Console.WriteLine("T2 finished waitting for AutoResetEvent ar1,at time {0}", DateTime.Now.ToLongTimeString());
Console.WriteLine("T2 starting to wait for AutoResetEvent ar2,at time {0}", DateTime.Now.ToLongTimeString());
ar2.WaitOne();
Console.WriteLine("T2 finished waiting for AutoResetEvent ar2,at time {0}", DateTime.Now.ToLongTimeString());
});
T1.Name = "T1";
T2.Name = "T2";
T1.Start();
T2.Start();
Console.Read();
}
}
}
Wait示例
Wait
private static object lockObject = new object();
static void Main(string[] args)
{
Console.WriteLine("主线程运行,线程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
Thread t = new Thread(DoWork);
t.Start();
Monitor.Enter(lockObject);
Console.WriteLine("主线程得到锁,开始运行");
Console.WriteLine("主线程释放锁,开始等待");
Monitor.Wait(lockObject);
Console.WriteLine("主线程重新得到锁,结束等待");//这句得等到worker线程pulse之后才能执行
Console.Read();
}
static void DoWork()
{
Console.WriteLine("worker线程运行,线程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
Console.WriteLine("停顿10秒");
int i = 1;
while (i <= 10)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
i++;
if (i == 5)
{
Monitor.Enter(lockObject);
Monitor.Pulse(lockObject);
Monitor.Exit(lockObject);
}
}
}
买书示例:
代码
下面我们来举一个例子:我去书店买书,当我选中一本书后我会去收费处付钱,
付好钱后再去仓库取书。这个顺序不能颠倒,我作为主线程,收费处和仓库做两个辅助线程,代码如下:
using System;
using System.Linq;
using System.Activities;
using System.Activities.Statements;
using System.Threading;
namespace CaryAREDemo
{
class Me
{
const int numIterations = 550;
static AutoResetEvent myResetEvent = new AutoResetEvent(false);
static AutoResetEvent ChangeEvent = new AutoResetEvent(false);
//static ManualResetEvent myResetEvent = new ManualResetEvent(false);
//static ManualResetEvent ChangeEvent = new ManualResetEvent(false);
static int number; //这是关键资源
static void Main()
{
Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc));
payMoneyThread.Name = "付钱线程";
Thread getBookThread = new Thread(new ThreadStart(GetBookProc));
getBookThread.Name = "取书线程";
payMoneyThread.Start();
getBookThread.Start();
for (int i = 1; i <= numIterations; i++)
{
Console.WriteLine("买书线程:数量{0}", i);
number = i;
//Signal that a value has been written.
myResetEvent.Set();
ChangeEvent.Set();
Thread.Sleep(0);
}
payMoneyThread.Abort();
getBookThread.Abort();
}
static void PayMoneyProc()
{
while (true)
{
myResetEvent.WaitOne();
//myResetEvent.Reset();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
}
}
static void GetBookProc()
{
while (true)
{
ChangeEvent.WaitOne();
// ChangeEvent.Reset();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
Console.WriteLine("------------------------------------------");
Thread.Sleep(0);
}
}
}
}
运行结果如下: