线程系列01,前台线程,后台线程,线程同步

在控制台应用程序集中,Main方法开始的是一个线程。如果要再创建线程,需要用到System.Threading这个命名空间。

 

□ 线程是怎样工作的?


CLR维护着一个叫"thread scheduler"的机制,这个机制与操作系统交互。"thread scheduler"保证所有的线程有合适的执行时间,那些等待或被阻塞的线程不会占有CPU时间。
在单处理器计算机上,"thread scheduler"在几十毫秒的时间内切换当前线程的执行。在多处理器计算机上,"thread scheduler"一方面也会扮演在单处理器上的角色,此外,还会同时把不同的线程交给不同的CPU处理。值得注意的是:使用多线程并不是总是好的。

 

□ 线程和进程

 

一台运行的电脑上可能会有多个进程,一个运行的进程可能会有多个线程。进程之间是完全隔离的,但在同一应用程序中不同线程可以共享数据。线程之间可以协同工作,比如一个后台线程用来获取数据,当获取到数据之后,另一个线程用来显示数据。

 

□ 多线程的应用场景

 

○ 保持一个更快响应的UI界面:让UI线程只处理鼠标和键盘触发事件,其它线程处理其它事件。
○ 有效利用CPU:一个线程被阻塞,该线程就不会占有CPU资源。
○ 并行编程:执行一个比较耗时的任务时,可以分摊给多个线程。
○ 投机执行:让一个有可能被执行的线程先提前执行;让不同的线程用不同的算法,最先得出结果的胜出。
○ 请求被同步处理:无论是在客户端还是在服务端,可以用多线程同时处理多个并发请求。

 

□ 创建第一个线程

using System;
using System.Threading;
namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            var thread = new Thread(DoSth);
            thread.Start();
        }
        static void DoSth()
        {
            Console.WriteLine("我来自另外一个线程");
        }
    }
}

 

实际上,new Thread(ThreadStart del)中的形参是委托类型:

public delegate void ThreadStart();

 

既然是委托,那在声明委托的时候,同样可以使用委托的构造函数:

var thread = new Thread(new ThreadStart (DoSth))

 

如果DoSth方法带参数,可以这样写:

var thread = new Thread(new ParameterizedThreadStart(DoSth));

 

□ 前台线程和后台线程

新建的线程在默认情况下是前台线程,可以通过把IsBackground属性设置为true,把线程定义为后台线程,一旦定义成后台线程,只要前台线程结束,无论后台线程是否结束,应用程序进程结束。

using System;
using System.Threading;
namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            var thread = new Thread(DoSth);
            thread.IsBackground = true;
            thread.Start(1);
            Console.WriteLine("离开主线程");
        }
        static void DoSth(object threadId)
        {
            Console.WriteLine("我来自另外一个线程" + threadId);
        }
    }
}

○ ManagedThreadId属性,托管线程Id,在进程内唯一,与操作系统的线程Id不是一回事。
○ Start方法可以带参数,参数将被传递到线程方法
○ IsBackground属性,设置线程是否为后台线程

 

默认情况下,创建的线程都是前台线程,只要有一个前台线程在运行,运用程序就不会停止;如果有些线程的是后台线程,当所有的前台线程运行完毕,应用程序就会停止,所有的后台线程也必须同时被终止。

 

□ 线程同步

※ 线程不同步的问题

假设主程序中有一个静态变量,在主程序的方法内无限循环,每次让该静态变量自增1。

如果把该方法交给一个线程。

    class Program
    {
        private static int count = 0;
        static void Main(string[] args)
        {
            var t1 = new Thread(AddCount);
            t1.Start();
        }
        static void AddCount()
        {
            while (true)
            {
                int temp = count;
                Thread.Sleep(1000);
                count = temp + 1;
                Console.WriteLine("我的托管线程ID为:" + Thread.CurrentThread.ManagedThreadId + " 目前count的值为:" + count);
                Thread.Sleep(1000);
            }
        }
    }

运行良好,显示的count值是连续递增1。

 

如果把该方法交给2个线程。

    class Program
    {
        private static int count = 0;
        static void Main(string[] args)
        {
            var t1 = new Thread(AddCount);
            var t2 = new Thread(AddCount);
            t1.Start();
            t2.Start();
        }
        ......
    }

我们发现,count的值不是递增。也就是说,count的值没有做到同步。

1

→进入线程1,temp=0,线程1开始sleep
→进入线程2,temp=0,线程2开始sleep
→线程1"醒来",让count=1,显示count值为1,又sleep
→线程2"醒来",temp还是为0,所以count还是为1,显示count值为1,又sleep
→如此循环
这里的问题是:本想让count一直递增,但线程1和线程2没有适时同步。如何解决呢?

 

※ 让线程同步

使用lock语句块,可以让2个线程同步,让每次只有一个线程进入程序执行的某个部分。

    class Program
    {
        private static int count = 0;
        static object o = new object();
        static void Main(string[] args)
        {
            var t1 = new Thread(AddCount);
            var t2 = new Thread(AddCount);
            t1.Start();
            t2.Start();
        }
        static void AddCount()
        {
            while (true)
            {
                lock (o)
                {
                    int temp = count;
                    Thread.Sleep(1000);
                    count = temp + 1;
                    Console.WriteLine("我的托管线程ID为:" + Thread.CurrentThread.ManagedThreadId + " 目前count的值为:" + count);
                }            
                Thread.Sleep(1000);
            }
        }
    }

2

 

总结:
○ 如果允许一个主线程结束,其它线程不管执行情况如何都结束,就把其它线程设置为后台线程。
○ lock语句块能保证线程同步

 

线程系列包括:

线程系列01,前台线程,后台线程,线程同步

线程系列02,多个线程同时处理一个耗时较长的任务以节省时间

线程系列03,多线程共享数据,多线程不共享数据

线程系列04,传递数据给线程,线程命名,线程异常处理,线程池

线程系列05,手动结束线程

线程系列06,通过CLR代码查看线程池及其线程

线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步

线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

线程系列09,线程的等待、通知,以及手动控制线程数量

线程系列10,无需显式调用线程的情形

posted @ 2014-09-18 17:10  Darren Ji  阅读(1308)  评论(3编辑  收藏  举报

我的公众号:新语新世界,欢迎关注。