《C#高级编程》读书笔记(十五):任务、线程和同步之三 Thread类

    Thread类允许创建前台线程,以及设置线程的优先级。使用Thread类可以创建和控制线程。

    在默认情况下,用Thread类创建的线程是前台线程线程池中的线程总是后台线程

        static void Main(string[] args)
        {
            var t1 = new Thread(ThreadMain);
            t1.Start();
            Console.WriteLine("This is the main thread.");
            Console.ReadKey();
        }

        static void ThreadMain()
        {
            Console.WriteLine("Running in a thread.");
        }

    不能保证哪个结果先输出。线程有操作系统调度,每次哪个线程在前面可以不同。

    也可以用Lambda表达式:

        static void Main(string[] args)
        {
            var t1 = new Thread(()=>Console.WriteLine(
                $"running in a thread,id: {Thread.CurrentThread.ManagedThreadId}"));
            t1.Start();
            Console.WriteLine($"This is the main thread,id:{Thread.CurrentThread.ManagedThreadId}");
            Console.ReadKey();
        }

1,后台线程

    在用Thread类创建线程时,可以设置IsBackground属性,以确定该线程是前台线程还是后台线程。

var t1 = new Thread(ThreadMain)
           { Name = "MyNewThread",IsBackground = false};

2,线程的优先级

    线程有操作系统调度。由线程指定优先级,就可以影响调度顺序。

    在Thread类中,可以设置Priority属性,以影响线程的基本优先级。

3,争用条件

    如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件。

    用一个例子来说明争用条件:

public class StateObject
    {
        private int state = 5;

        public void ChangeState(int loop)
        {
            if (state == 5)
            {
                state++;
                Trace.Assert(state == 6,"Race condition occurred after"+loop+"loops");
            }
            state = 5;
        }
    }
public class SampleTask
    {
        public void RaceCondition(object o)
        {
            Trace.Assert(o is object,"o must be of type StateObject");
            StateObject state = o as StateObject;

            int i = 0;
            while (true)
            {
                state.ChangeState(i++);
            }
        }
    }
static void Main(string[] args)
        {
          var state = new StateObject();
            for (int i = 0; i < 2; i++)
            {
                Task.Run(() => new SampleTask().RaceCondition(state));
            }
           Console.ReadKey();
        }

    要避免该问题,可以用lock语句锁定在线程中共享的state变量。

    只有引用类型才能用于锁定,因为值类型没有SyncBlock字段,lock本质上Monitor.Enter,Monitor.Enter会使值类型装箱,每次lock的是装箱后的对象。

4,死锁

     过多的锁定也会有麻烦。在死锁中,至少有两个线程被挂起,并等待对方解除锁定。由于两个线程都在等在对方,就出现了死锁,线程将无限等待下去。

public class SampleTask
    {
        private StateObject s1, s2;
        public SampleTask(StateObject s1, StateObject s2)
        {
            this.s1 = s1;
            this.s2 = s2;
        }

        public void Deadlock1()
        {
            int i = 0;
            while (true)
            {
                lock (s1)
                {
                    lock (s2)
                    {
                        s1.ChangeState(i);
                        s2.ChangeState(i++);
                        Console.WriteLine($"still running,{i}");
                    }
                }
            }
        }

        public void Deadlock2()
        {
            int i = 0;
            while (true)
            {
                lock (s2)
                {
                    lock (s1)
                    {
                        s1.ChangeState(i);
                        s2.ChangeState(i++);
                        Console.WriteLine($"still running,{i}");
                    }
                }
            }
        }

    }

 

            var state1 = new StateObject();
            var state2 = new StateObject();
            new Task(new SampleTask(state1,state2).Deadlock1).Start();
            new Task(new SampleTask(state1, state2).Deadlock2).Start();

    F5运行,然后点击“全部中断”,再打开调试->窗口->任务,就可以看到,线程出于死锁状态。

posted @ 2016-08-24 22:38  逍遥king  阅读(245)  评论(0编辑  收藏  举报