第16章 多线程

16.1 线程的概念

16.2 线程的基本操作

16.2.1 创建新线程

Thread th = new Thread()

线程有以下几个常用的属性:

ManagedThreadId属性,托管线程ID,在进程内唯一

name属性,默认为空

ThreadState属性,是一个位标记,描述了线程的当前运行状态:Background、Unstarted、Running、WaitSleepJoin、Stopped。

16.2.2 查看当前线程

Thread.CurrentThread.Name

16.2.3 Sleep()方法

public static void Sleep(int millisecondsTimeout);

public static void Sleep(TimeSpan timeout);

16.2.4 Interrupt()方法

16.2.5 前台线程和后台线程

前台后台区别:所有前台线程执行完毕后,应用程序进程结束,而无论后台进程是否结束。

容易混淆的一个地方:后台线程的background标记就相当于前台线程的running标记,而不是background,running标记

在创建一个线程之后,就应该对它的生存周期有完全的掌控,因此,不去管理后台线程,让它随主线程的结束而终结是很欠妥的做法,尤其是当后台线程还持有一些资源需要关闭时。当后台线程以这种方式退出时,即使位于finally块中的语句也不会执行。

16.2.6 Join()方法

等待线程执行结束后,在继续执行后面的代码

16.2.7 Suspend()和Resume()方法

用于挂起和继续执行已挂起的线程(不推荐使用)

16.2.8 线程异常

当工作线程抛出异常时,整个进程都会关闭,而不是仅结束抛出异常的线程(调用abort方法抛出ThreadAbortException异常除外)

16.2.9 Abort()方法

如果想要强制退出一个线程,可以调用Abort()方法,调用Abort()方法会抛出一个System.Threading.ThreadAbortException异常,该异常很特殊,因为即使不捕获它,也不会影响到整个进程。

16.3 线程同步

线程同步就是协调多个线程间的并发操作,以获得预期的确定的执行结果,消除多线程应用程序执行中的不确定性,包含两方面:

1、保护资源,确保资源同时只能有一个(或指定个数)的线程访问,一般措施是获取锁和释放锁(锁机制)

2、协调线程对资源的访问顺序,即确定某一资源只能现有线程A访问,再由线程B访问,一般措施是采用信号量机制。当B线程访问资源时,必须等待线程A先访问,线程A访问完后,发出信号量,通知线程B可以访问

16.3.1 使用Monitor

1.使用对象本身作为锁对象

Monitor只能对引用类型加锁,否则会抛错

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        Resource res = new Resource();
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.ThreadEntity);
            th.Name = "Worker";
            th.Start();
            p.ThreadEntity();
            Console.Read();
        }

        void ThreadEntity()
        {
            Monitor.Enter(res);
            res.Record();
            Monitor.Exit(res);
        }
    }

    public class Resource
    {
        public string call;
        public void Record()
        {
            call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond);
            Console.WriteLine(call);
        }
    }
}
View Code

2.使用System.Object作为锁对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        Resource res = new Resource();
        private object lockObject = new object();
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.ThreadEntity);
            th.Name = "Worker";
            th.Start();
            p.ThreadEntity();
            Console.Read();
        }

        void ThreadEntity()
        {
            Monitor.Enter(lockObject);
            res.Record();
            Monitor.Exit(lockObject);
        }
    }

    public struct Resource
    {
        public string call;
        public void Record()
        {
            call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond);
            Console.WriteLine(call);
        }
    }
}
View Code

3.使用System.Type作为锁对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        private object lockObject = new object();
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.ThreadEntity);
            th.Name = "Worker";
            th.Start();
            p.ThreadEntity();
            Console.Read();
        }

        void ThreadEntity()
        {
            Monitor.Enter(typeof(Resource));
            Resource.Record();
            Monitor.Exit(typeof(Resource));
        }
    }

    public static class Resource
    {
        public static string call;
        public static void Record()
        {
            call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond);
            Console.WriteLine(call);
        }
    }
}
View Code

4.使用lock语句

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        Resource res = new Resource();
        private object lockObject = new object();
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.ThreadEntity);
            th.Name = "Worker";
            //th.IsBackground = true;
            th.Start();
            p.ThreadEntity();
            Console.Read();
        }

        void ThreadEntity()
        {
            lock (res)
            {
                try
                {
                    Resource.Record();
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }

    public class Resource
    {
        public static string call;
        public static void Record()
        {
            call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond);
            Console.WriteLine(call);
            throw new Exception();
        }
    }
}
View Code

5.创建线程安全类型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{
    class Program
    {
        Resource res = new Resource();
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.ThreadEntity);
            th.Name = "Worker";
            //th.IsBackground = true;
            th.Start();
            p.ThreadEntity();
            Console.Read();
        }

        void ThreadEntity()
        {
            res.Record();
        }
    }

    public class Resource
    {
        public string call;
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Record()
        {
            call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond);
            Console.WriteLine(call);
        }
    }
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{
    class Program
    {
        static Resource res = new Resource();
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.ThreadEntity);
            th.Name = "Worker";
            //th.IsBackground = true;
            th.Start();
            p.ThreadEntity();
            //Console.WriteLine(res.Index);
            Console.Read();
        }

        void ThreadEntity()
        {
            lock (res)
            {
                for (int i = 0; i <= 2; i++)
                {
                    lock (res)
                    {
                        res.Index = res.Index + 1;
                        Console.WriteLine("{0}:{1}", Thread.CurrentThread.Name, res.Index);
                    }
                }
            }
        }
    }

    public class Resource
    {
        private int _index;
        public int Index
        {
            get
            {
                lock (this)
                {
                    return _index;
                }
            }
            set
            {
                lock (this)
                {
                    _index = value;
                }
            }
        }
    }
}
View Code

锁的粒度对于程序执行的顺序和结果是很重要的。

6.使用Monitor协调线程执行顺序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{
    class Program
    {
        Resource res = new Resource();
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.ThreadEntity);
            th.Name = "Worker";
            //th.IsBackground = true;
            th.Start();
            //Thread.Sleep(1000);
            lock (p.res)
            {
                if (string.IsNullOrEmpty(p.res.data))
                {
                    bool isTimeout = Monitor.Wait(p.res, 100);
                    Console.WriteLine(isTimeout);
                }
                Console.WriteLine("data={0}", p.res.data);
            }
            //Console.WriteLine(res.Index);
            Console.Read();
        }

        void ThreadEntity()
        {
            lock (res)
            {
                res.data = "Retrived";
                Monitor.Pulse(res);
            }
        }
    }

    public class Resource
    {
        public string data;
    }
}
View Code

 7.死锁

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{
    class Program
    {
        Resource mainRes = new Resource() { data = "Main" };
        Resource workerRes = new Resource() { data = "Worker" };
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.T2);
            th.Name = "Worker";
            //th.IsBackground = true;
            th.Start();
            T1(p);
            Console.Read();
        }

        static void T1(Program p)
        {
            lock (p.mainRes)
            {
                Thread.Sleep(10);
                lock (p.workerRes)
                {
                    Console.WriteLine(p.workerRes.data);
                }
            }
        }

        void T2()
        {
            lock (workerRes)
            {
                Thread.Sleep(10);
                lock (mainRes)
                {
                    Console.WriteLine(mainRes.data);
                }
            }
        }
    }

    public class Resource
    {
        public string data;
    }
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{
    class Program
    {
        Resource mainRes = new Resource() { data = "Main" };
        Resource workerRes = new Resource() { data = "Worker" };
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";
            Program p = new Program();
            Thread th = new Thread(p.T2);
            th.Name = "Worker";
            //th.IsBackground = true;
            th.Start();
            T1(p);
            Console.Read();
        }

        static void T1(Program p)
        {
            lock (p.mainRes)
            {
                Thread.Sleep(10);
                int i = 0;
                while (i < 3)
                {
                    if (Monitor.TryEnter(p.workerRes))
                    {
                        Console.WriteLine(p.workerRes.data);
                        Monitor.Exit(p.workerRes);
                        break;
                    }
                    else
                    {
                        i++;
                        Thread.Sleep(1000);
                    }
                }
                if (i == 3)
                {
                    Console.WriteLine("{0}:deadlock", Thread.CurrentThread.Name);
                }
            }
        }

        void T2()
        {
            lock (workerRes)
            {
                Thread.Sleep(10);
                int i = 0;
                while (i < 3)
                {
                    if (Monitor.TryEnter(mainRes))
                    {
                        Console.WriteLine(workerRes.data);
                        Monitor.Exit(mainRes);
                        break;
                    }
                    else
                    {
                        i++;
                        Thread.Sleep(1000);
                    }
                }
                if (i == 3)
                {
                    Console.WriteLine("{0}:deadlock", Thread.CurrentThread.Name);
                }
            }
        }
    }

    public class Resource
    {
        public string data;
    }
}
View Code
原文地址:https://www.cnblogs.com/liuslayer/p/5405257.html