您的位置:首页 > 编程语言 > C#

C#使用_多线程处理

2008-09-07 13:23 281 查看
以下内容均来自(C#高级编程学习捷径 刘洪成 编著 清华大学出版社)
1. 创建一个线程
using System;
using System.Threading;

namespace ConsoleApplication1
{
class App
{
public void run()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i.ToString());
}
}

static void Main(string[] args)
{
Console.WriteLine("App Start");

App app = new App();
ThreadStart ts = new ThreadStart(app.run);
Thread t = new Thread(ts);

t.Start();
Console.WriteLine("App end");
}
}
}

2. 为线程设置名字
Thread有Name属性,可以设置它的名字。

3. Sleep函数
Sleep函数被调用时,该线程所占用的系统资源,一般来说是CPU,会被释放掉,从而其他线程可以得到一些资源以进行响应的操作。

4. 设置线程优先级
可以通过设置Priority属性来控制线程的优先级。但是我们对线程的运行次序永远无法做到真正控制,因为这个控制权主要在于操作系统。我们只能在整体上使一个线程获得更多或更少的运行机会而已。而并不是说在任何一个特定的时刻,一个高优先级的线程就一定会先于一个低优先级的线程被运行。

5. 线程的后台与前台运行
所谓运行在后台是说该线程与产生它的父进程紧密联系在一起,它隐藏在父进程的后面,所以一旦父进程终止运行,该线程本身也会被立即终止,不管线程是否已经真正结束。相反,一个运行在前端的线程却是独立于产生它的父进程,一旦该进程开始运行,即使父进程已经终止运行,这个线程却会照样独立运行,直到运行结束或者被其他程序强制终止。C#中的一个线程的默认设置为前台运行。

6. 终止一个进程
6.1我们可以使用Abort方法来强行终止一个线程
6.2 一个更好的方法是通过设置一个标志来告诉线程自动终止,程序如下
using System;
using System.Threading;

namespace ConsoleApplication1
{
class App
{
private bool bContinue = true;

public void run()
{
Thread t = Thread.CurrentThread;
for (int i = 0; i < 10; i++)
{
Console.WriteLine(t.Name + ": " + i.ToString());
Thread.Sleep(1);
if (!bContinue)
{
return;
}
}
}

static void Main(string[] args)
{
Console.WriteLine("App Start");

App app = new App();
ThreadStart ts1 = new ThreadStart(app.run);
Thread t1 = new Thread(ts1);

t1.Name = "T1";
t1.Start();

Thread.Sleep(5);

app.bContinue = false;

Console.WriteLine("App end");
}
}
}

7. 线程的暂停与再运行
使用Suspend方法,我们可以暂停一个线程,如果要重新激活它,则使用Resume方法。

8. 线程Join函数
在激活一个线程后,我们可以等待该线程运行结束,或者设置一个最长的等待时间。只有等线程自动结束或者已经运行超过了前面预先设定的时间后,才去接着运行后面的程序。
using System;
using System.Threading;

namespace ConsoleApplication1
{
class App
{
public void run()
{
Thread t = Thread.CurrentThread;
for (int i = 0; i < 10; i++)
{
Console.WriteLine(t.Name + ": " + i.ToString());
Thread.Sleep(10);
}
}

static void Main(string[] args)
{
Console.WriteLine("App Start");

App app = new App();
ThreadStart ts1 = new ThreadStart(app.run);
ThreadStart ts2 = new ThreadStart(app.run);
Thread t1 = new Thread(ts1);
Thread t2 = new Thread(ts2);

t1.Name = "T1";
t2.Name = "T2";
t1.Start();
t1.Join(40);
t2.Start();
t2.Join();

Console.WriteLine("App end");
}
}
}
在这个程序中,T1运行40微秒后,T2才有机会运行,而主程序则是一直等待T2结束后,才去运行最后一个打印语句。

9. 线程加锁
线程加锁可以放置同一时刻,多个线程对资源的争夺。使它们能够正确的访问资源。
using System;
using System.Threading;

namespace ConsoleApplication1
{
class App
{
public int i = 0;
public int j = 0;
public int Limit = 100;
public void Increase()
{
while (i < Limit)
{
i++;
Thread.Sleep(1);
j++;
}
}

public void Check()
{
while (i < Limit)
{
if (i != j)
{
Console.WriteLine("i != j, i = {0}, j = {1}", i, j);
Thread t = Thread.CurrentThread;
t.Abort();
}
Thread.Sleep(1);
}
}

static void Main(string[] args)
{
App app = new App();

Thread t1 = new Thread(new ThreadStart(app.Increase));
Thread t2 = new Thread(new ThreadStart(app.Increase));
Thread t3 = new Thread(new ThreadStart(app.Check));

t1.Start();
t2.Start();
t3.Start();
}
}
}
在这个例子中,我们看到,每当i被加1时,j也总是被同时加1,所以看来i与j应该永远相等,可事实上,我们得到如下结果:
i != j, i = 2, j = 0
原因在于,i与j的累加和i与j的检测发生在两个不同的线程中。可能发生的情况是,在一愕嘎线程中i刚被累加过,但是j还没有;或者是前两个线程互相覆盖了累加的结果(假设在某一点上,线程A与线程B同时读取i的值为5,此时,j的值也是5,但两个线程都还没有去读取它,然后线程A将i加上1,即i的值变为6,可是同样的,线程B又在5的基础上将i加1,再次将i设置为6,所以i最终被设置为6。但是两个线程对j的读取却是错开进行的,也就是说当线程A将j的值改变为6后,线程B才去读取,从而发现j的值为6,并在此基础上将其加1,最终将j的值设定为7)。这样在测试程序中,便发现i与j的值不等了。这便是线程之间的资源共享问题。
C#为解决资源共享问题引进了Monitor的概念,也就是说,在某个线程要运行一段程序前先得获取一个监控器,从而通知其他线程不可能同时再运行这同一段程序。比如,上面的例子中的Increase函数可以修改如下:
public void Increase()
{
while (i < Limit)
{
try
{
Monitor.Enter(this);
i++;
Thread.Sleep(1);
j++;
}
finally
{
Monitor.Exit(this);
}
}
}
这样便解决了两个累加线程之间相互覆盖的问题。可是这样i与j还是可能不等,我们必须给检测函数也加一个监控器。这样无论何时,ij总是相等的了。
再实际应用中,我们总是将Monitor.Enter与Monitor.Exit同时运用,C#又提供了lock语句来实现对一段程序的加锁,因而程序可以简化为
public void Increase()
{
while (i < Limit)
{
lock (this)
{
i++;
Thread.Sleep(1);
j++;
}
}
}
同样要lock住Check函数。
注意:虽然我们对这两个函数加了锁,但是我们还是不能保证任何适合ij的值在任何程序中都是相等的。比如我们添加一个函数,然后添加一个线程。ij就可能不等了。所以,最彻底的解决办法是,对i和j的访问加锁。最终的程序如下:
using System;
using System.Threading;

namespace ConsoleApplication1
{
class App
{
public int i = 0;
public int j = 0;
public int Limit = 100;
public void Increase()
{
while (i < Limit)
{
lock (this)
{
i++;
Thread.Sleep(1);
j++;
}
}
}

public int I
{
get
{
lock (this)
{
return i;
}
}
set
{
lock (this)
{
i = value;
}
}
}

public int J
{
get
{
lock (this)
{
return j;
}
}
set
{
lock (this)
{
j = value;
}
}
}

public void Check()
{
while (I < Limit)
{
if (I != J)
{
Console.WriteLine("I != J, I = {0}, J = {1}", I, J);
Thread t = Thread.CurrentThread;
t.Abort();
}
Thread.Sleep(1);
}
}

static void Main(string[] args)
{
App app = new App();

Thread t1 = new Thread(new ThreadStart(app.Increase));
Thread t2 = new Thread(new ThreadStart(app.Increase));
Thread t3 = new Thread(new ThreadStart(app.Check));

t1.Start();
t2.Start();
t3.Start();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: