澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

澳门新萄京官方网站:高级编程,进程内部的同

2019-06-01 作者:www.8455.com   |   浏览(170)

  在线程里,如果需要共享数据,那么一定需要使用同步技术,确保一次只有一个线程访问和改变共享数据的状态。在.net中,lock语句、Interlocked类和Monitor类可用于进程内部的同步。


1、如果两个或多个线程访问相同的对象,或者访问不同步的共享状态,会出现争用条件。

(一)概述

(一)概述

1、lock语句与线程安全

  lock语句是设置锁定和解除锁定的一种简单方式。在使用lock语句之前,先进入另一个争用条件。例如:

public class SharedState
{
    public int State { get; set; }
}
public class Job
{
    SharedState sharedState;
    public Job(SharedState sharedState)
    {
        this.sharedState = sharedState;
    }
    public void DoTheJob()
    {
        for (int i = 0; i < 50000; i  )
        {
                sharedState.State  = 1;
        }
    }
}
static void Main()
{
    int numTasks = 20;
    var state = new SharedState();
    var tasks = new Task[numTasks];//定义20个任务

    for (int i = 0; i < numTasks; i  )
    {
        tasks[i] = Task.Run(() => new Job(state).DoTheJob());//启动20个任务,同时对数据进行修改
    }

    for (int i = 0; i < numTasks; i  )
    {
        tasks[i].Wait();//等待所有任务结束
    }

    Console.WriteLine("summarized {0}", state.State);//预想应该输出:summarized 1000000
}

  实际上的输出与预想输出并不一致,每次运行的输出结果都不同,但没有一个是正确的。如果将线程数量减少,那么得到正确值的次数会增多,但也不是每次都正确。

  使用lock关键字,可以实现多个线程访问同一个数据时的同步问题。lock语句表示等待指定对象的锁定,该对象只能时引用类型。进行锁定后——只锁定了一个线程,就运行lock语句块中的代码,在lock块最后接触锁定,以便另一个线程可以锁定该对象。

lock(obj)
{
    //执行代码
}
//锁定静态成员,可以所以其类型(object)
lock(typeof(StaticCalss))
{
    //执行代码
}

  所以修改以上的代码,使用SyncRoot模式。但是,如果是对属性的访问进行锁定:

public class SharedState
{
    private object syncRoot = new object();

    private int state = 0;
    public int State
    {
        get { lock (syncRoot) return state; }
        set { lock (syncRoot) state = value; }
    }
}

  仍会出现前面的争用情况。在方法调用get存储器,以获得state的当前值,然后set存储器给state设置新值。在调用对象的get和set存储器期间,对象并没有锁定,另一个线程仍然可以获得临时值。最好的方法是在不改变SharedState类的前提下,在调用方法中,将lock语句添加到合适的地方:

public class SharedState
{
    public int State { get; set; }
}
public class Job
{
    SharedState sharedState;
    public Job(SharedState sharedState)
    {
        this.sharedState = sharedState;
    }
    public void DoTheJob()
    {
        for (int i = 0; i < 50000; i  )
        {
            lock (sharedState)
            {
                sharedState.State  = 1;
            }
        }
    }
}

澳门新萄京官方网站:高级编程,进程内部的同步。  在一个地方使用lock语句并不意味着访问对象的其他线程都在等待。必须对每个访问共享数据的线程显示使用同步功能。

  为使对state的修改作为一个原子操作,修改代码:

public class SharedState
{
    private int state = 0;
    public int State { get { return state; } }
    public int IncrementState()
    {
        lock (this)
        {
            return   state;
        }
    }
}
//外部访问
public void DoTheJob()
{
    for (int i = 0; i < 50000; i  )
    {
         sharedState.IncrementState();        
    }
}

 1 using System;
 2 using System.Text;
 3 using System.Threading;
 4 
 5 class Outputer
 6 {
 7     public void Output(string msg)
 8     {
 9         for (int i = 0; i < msg.Length; i  )
10         {
11             Console.Write(msg[i]);
12         }
13         Console.WriteLine();
14     }
15 }
16 
17 class Program
18 {
19     static void Main(string[] args)
20     {
21         Outputer outputer = new Outputer();
22         object locker = new object();
23         StringBuilder str = new StringBuilder();
24         for (int i = 0; i < 26; i  )
25         {
26             str.Append(((char)('A'   i)).ToString());
27         }
28         new Thread((msg) =>
29         {
30             while (true)
31             {
32                 outputer.Output(msg.ToString());
33             }
34         }).Start(str.ToString());
35         new Thread(() =>
36         {
37             while (true)
38             {
39                 outputer.Output("1234567890");
40             }
41         }).Start();
42     }
43 }

所有需要等待的操作,例如,因为文件、数据库或网络访问都需要一定的时间,此时就可以启动一个新的线程,同时完成其他任务。

所有需要等待的操作,例如,因为文件、数据库或网络访问都需要一定的时间,此时就可以启动一个新的线程,同时完成其他任务。

2、Interlocked类

  Interlocked类用于使变量的简单语句原子化。i 并非线程安全的,它涉及三个步骤:取值、自增、存值。这些操作可能被线程调度器打断。Interlocked类提供了以线程安全的方式递增、递减、交换和读取值的方法。Interlocked类只能用于简单的同步问题,而且很快。因此,上面的IncrementState()方法的代码可以改为:return Interlocked.Increment(ref state);

多线程间应尽量避免同步问题,最好不要线程间共享数据。如果必须要共享数据,就需要使用同步技术,确保一次只有一个线程访问和改变共享状态。

运行结果:

线程是程序中独立的指令流。

线程是程序中独立的指令流。

3、Monitor类

  lcok语句最终会有C#编译器解析为使用Monitor类。

lock(obj)
{
    //执行代码
}

  简单的lock(obj)语句会被解析为调用Enter()方法,该方法会一直等待,直到线程锁定对象。一次只有一个线程能锁定对象,只要解除锁定,线程就可以进入同步阶段。Monitor类的Exit()方法解除锁定。编译器把Exit()方法放在try块的finally中,不论是否抛出异常,都将在语句块运行末尾解除锁定。

Monitor.Enter(obj);
try
{
    //执行代码
}
finally
{
    Monitor.Exit(obj);
}

  相对于lock语句,Mpnitor类可以设置一个等待被锁定的超时值。这样就不会无限期的等待锁定,如果等待锁定时间超过规定时间,则返回false,表示未被锁定,线程不再等待,执行其他操作。也许以后,该线程会再次尝试获得锁定:

bool lockTaken = false;
Monitor.TryEnter(obj,500, ref lockTaken);//在500ms内,是否锁定了对象
if (lockTaken)
{
    try
    {
        //执行代码
    }
    finally
    {
        Monitor.Exit(obj);
    }
}
else
{
    //未获得锁定,执行代码
}

   如果基于对象的锁定对象(Monitor)的系统开销由于垃圾回收而过高,可以使用SpinLock结构。,SpinLock结构适用于:有大量的锁定,而且锁定时间总是非常短的情况。应避免使用多个SpinLock结构,也不要调用任何可能阻塞的内容。

一::lock语句

澳门新萄京官方网站 1

 

 

lock语句事设置锁定和接触锁定的一种简单方法。其语法非常简单:

2、要避免该问题,可以使用lock语句锁定共享的对象。

(二)Paraller类

(二)Paraller类

            lock (obj)
            {
                // 需要发生同步的代码区
            }
 1 using System;
 2 using System.Text;
 3 using System.Threading;
 4 
 5 class Outputer
 6 {
 7     public void Output(string msg)
 8     {
 9         for (int i = 0; i < msg.Length; i  )
10         {
11             Console.Write(msg[i]);
12         }
13         Console.WriteLine();
14     }
15 }
16 
17 class Program
18 {
19     static void Main(string[] args)
20     {
21         Outputer outputer = new Outputer();
22         object locker = new object();
23         StringBuilder str = new StringBuilder();
24         for (int i = 0; i < 26; i  )
25         {
26             str.Append(((char)('A'   i)).ToString());
27         }
28         new Thread((msg) =>
29         {
30             while (true)
31             {
32                 lock (locker)
33                 {
34                     outputer.Output(msg.ToString());
35                 }
36             }
37         }).Start(str.ToString());
38         new Thread(() =>
39         {
40             while (true)
41             {
42                 lock (locker)
43                 {
44                     outputer.Output("1234567890");
45                 }
46             }
47         }).Start();
48     }
49 }

Paraller类是对线程的一个很好的抽象,该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性。

Paraller类是对线程的一个很好的抽象,该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性。

将共享数据的操作代码,放在上述的“{...}”区域内。锁定的对象(obj)必须是引用类型,如果锁定一个值类型,实际是锁定了它的一个副本,并没有实现锁定功能。

运行结果:

Paraller.For()和Paraller.ForEach()方法在每次迭代中调用相同的代码,二Parallel.Invoke()方法允许同时调用不同的方法。Paraller.Invoke用于任务并行性,而Parallel.ForEach用于数据并行性。

Paraller.For()和Paraller.ForEach()方法在每次迭代中调用相同的代码,二Parallel.Invoke()方法允许同时调用不同的方法。Paraller.Invoke用于任务并行性,而Parallel.ForEach用于数据并行性。

一般地,被锁定对象需要被创建为 私有 只读 引用类型:

澳门新萄京官方网站 2

 

 

        private readonly object obj = new object();

3、也可以将共享对象设置为线程安全的对象。

1、Parallel.For()方法循环

1、Parallel.For()方法循环

二::Interlocked类

 1 using System;
 2 using System.Text;
 3 using System.Threading;
 4 
 5 class Outputer
 6 {
 7     public void Output(string msg)
 8     {
 9         lock (this)
10         {
11             for (int i = 0; i < msg.Length; i  )
12             {
13                 Console.Write(msg[i]);
14             }
15             Console.WriteLine();
16         }
17     }
18 }
19 
20 class Program
21 {
22     static void Main(string[] args)
23     {
24         Outputer outputer = new Outputer();
25         object locker = new object();
26         StringBuilder str = new StringBuilder();
27         for (int i = 0; i < 26; i  )
28         {
29             str.Append(((char)('A'   i)).ToString());
30         }
31         new Thread((msg) =>
32         {
33             while (true)
34             {
35                 outputer.Output(msg.ToString());
36             }
37         }).Start(str.ToString());
38         new Thread(() =>
39         {
40             while (true)
41             {
42                 outputer.Output("1234567890");
43             }
44         }).Start();
45     }
46 }
ParallelLoopResult result = Parallel.For(0, 10, i =>
{
    Console.WriteLine("当前迭代顺序:"   i);
    Thread.Sleep(10);//线程等待
});
ParallelLoopResult result = Parallel.For(0, 10, i =>
{
    Console.WriteLine("当前迭代顺序:"   i);
    Thread.Sleep(10);//线程等待
});

Interlocked类用于使变量的简单语句原子化。它提供了以线程安全的方式递增、递减、交换和读取值的方法。

4、过多的锁定会造成死锁。所谓死锁即是至少有两个线程被挂起,互相等待对方解锁,以至于线程无限等待下去。

在For()方法中,前两个参数定义了循环的开头和结束,第三个参数是一个Action<int>委托,参数是循环迭代的次数。

在For()方法中,前两个参数定义了循环的开头和结束,第三个参数是一个Action<int>委托,参数是循环迭代的次数。

        private int stateFlag = 0;

        public int IncrementState
        {
            //get
            //{
            //    lock (this)
            //    {
            //        stateFlag  ;
            //        return stateFlag;
            //    }
            //}

            get
            {
                return Interlocked.Increment(ref stateFlag); // using System.Threading;

                //Interlocked.Decrement(ref V0);
                //Interlocked.Exchange(ref V1, ref V2);
                //Interlocked.Read(ref V0);
            }
        }
 1 using System;
 2 using System.Threading;
 3 
 4 class DeadLocker
 5 {
 6     object locker1 = new object();
 7     object locker2 = new object();
 8 
 9     public void Method1()
10     {
11         while (true)
12         {
13             lock (locker1)
14             {
15                 lock (locker2)
16                 {
17                     Console.WriteLine("First lock1, and then lock2");
18                 }
19             }
20         }
21     }
22 
23     public void Method2()
24     {
25         while (true)
26         {
27             lock (locker2)
28             {
29                 lock (locker1)
30                 {
31                     Console.WriteLine("First lock2, and then lock1");
32                 }
33             }
34         }
35     }
36 }
37 
38 class Program
39 {
40     static void Main(string[] args)
41     {
42         DeadLocker dl = new DeadLocker();
43         new Thread(dl.Method1).Start();
44         new Thread(dl.Method2).Start();
45     }
46 }

Parallel类只等待它创建的任务,而不等待其他后台活动。

Parallel类只等待它创建的任务,而不等待其他后台活动。

三::Monitor类

运行结果:

Parallel.For()方法可以提前停止:

Parallel.For()方法可以提前停止:

与lock相似,C#的lock语句被编译器解析为使用Monitor类。锁定开始相当于 Monitor.Enter(obj) 方法,该方法会一直等待,直到线程被对象锁定。解除锁定后线程进入同步阶段,使用 Monitor.Exit(obj)方法解除锁定,编译器将它与try块的finally结合。方法一中的代码,相当于:

澳门新萄京官方网站 3

var result = Parallel.For(10, 40, async (int i, ParallelLoopState pls) =>
 {
     Console.WriteLine("迭代序号:{0}, 任务: {1}, 线程: {2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
     await Task.Delay(10);
     if (i > 15)
     {
         pls.Break();
     }
 });
Console.WriteLine("循环完成状态:"   result.IsCompleted);
Console.WriteLine("Break索引:"   result.LowestBreakIteration);
var result = Parallel.For(10, 40, async (int i, ParallelLoopState pls) =>
 {
     Console.WriteLine("迭代序号:{0}, 任务: {1}, 线程: {2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
     await Task.Delay(10);
     if (i > 15)
     {
         pls.Break();
     }
 });
Console.WriteLine("循环完成状态:"   result.IsCompleted);
Console.WriteLine("Break索引:"   result.LowestBreakIteration);
            Monitor.Enter(obj);
            try
            {
                // 需要发生同步的代码区
            }
            finally
            {
                Monitor.Exit(obj);
            }

5、同步问题和争用条件以及死锁相关,要避免同步问题,最好就不要在线程之间共享数据。如果要共享数据就必须使用同步技术,确保一次只有一个线程访问和改变共享状态。在C#中,lock语句是设置锁定和解除锁定的一种简单方式。编译器将其编译为IL后,会被编译成了调用Monitor类的Enter和Exit方法。

需要注意的是,Break()方法仅是告知循环在合适的时候退出当前迭代之外的迭代。

需要注意的是,Break()方法仅是告知循环在合适的时候退出当前迭代之外的迭代。

与lock语句相比,Monitor类的优点在于:可以添加一个等待北锁定的超时值。这样就不会无限期等待被锁定,而可以使用 TryEnter() 方法,给一个超时参数。

 1 using System;
 2 using System.Threading;
 3 
 4 class Program
 5 {
 6     static void Main(string[] args)
 7     {
 8     }
 9 
10     void Method()
11     {
12         lock (typeof(Program))
13         {
14         }
15     }
16 }

Parallel.For()还可以对线程进行初始化和退出时制定方法:

Parallel.For()还可以对线程进行初始化和退出时制定方法:

            bool lockTaken = false;
            Monitor.TryEnter(obj, 500, ref lockTaken);
            if (lockTaken)
            {
                try
                {
                    // acquired the lock
                    // synchronized region for obj
                }
                finally
                {
                    Monitor.Exit(obj);
                }
            }
            else
            {
                // didn't get the lock,do something else
            }

编译结果:

Parallel.For<string>(10,25,()=> {
    Console.WriteLine("初始线程{0},任务{1}",Thread.CurrentThread.ManagedThreadId,Task.CurrentId);
    return string.Format("线程Id"  Thread.CurrentThread.ManagedThreadId);
},
(i,pls,str1)=> {
    Console.WriteLine("迭代顺序:【{0}】,线程初始化返回值:【{1}】,线程Id:【{2}】,任务Id:【{3}】",i,str1, Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
    Thread.Sleep(10);
    return string.Format("迭代顺序:" i);
},
(str1)=> {
    Console.WriteLine("线程主体返回值:{0}",str1);
});
Parallel.For<string>(10,25,()=> {
    Console.WriteLine("初始线程{0},任务{1}",Thread.CurrentThread.ManagedThreadId,Task.CurrentId);
    return string.Format("线程Id"  Thread.CurrentThread.ManagedThreadId);
},
(i,pls,str1)=> {
    Console.WriteLine("迭代顺序:【{0}】,线程初始化返回值:【{1}】,线程Id:【{2}】,任务Id:【{3}】",i,str1, Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
    Thread.Sleep(10);
    return string.Format("迭代顺序:" i);
},
(str1)=> {
    Console.WriteLine("线程主体返回值:{0}",str1);
});

如果obj被锁定,TryEnter() 方法就会把 bool 型引用参数 lockTaken 设置为 true,并同步地访问由 obj 锁定的状态。如果另一线程 锁定 obj 的时间超过 500 毫秒,Try Enter() 方法就把变量 lockTaken 设为 false ,线程不再等待,而是用于执行其它操作。也许在之后,该线程会尝试再次被锁定。

澳门新萄京官方网站 4

除了循环开头与结束的指定,第三个是对迭代调用的每个线程进行处理,第四个是迭代的方法主体,第四个是迭代完成时对线程的处理。

澳门新萄京官方网站,除了循环开头与结束的指定,第三个是对迭代调用的每个线程进行处理,第四个是迭代的方法主体,第四个是迭代完成时对线程的处理。

 四::SpinLock结构

6、争用条件的另一个例子。

 

 

它是一个结构体(struct),用法极类似于Monitor类。获得锁用 Enter()或TryEnter() 方法,释放锁用 Exit() 方法。它还提供了属性 IsHeld 和 IsHeldByCurrentThred ,指定当前是否被锁定。

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 class SharedState
 6 {
 7     public int State { get; set; }
 8 }
 9 
10 class Worker
11 {
12     SharedState state;
13 
14     public Worker(SharedState state)
15     {
16         this.state = state;
17     }
18 
19     public void DoJob()
20     {
21         for (int i = 0; i < 500; i  )
22         {
23             state.State  = 1;
24         }
25     }
26 }
27 
28 class Program
29 {
30     static void Main(string[] args)
31     {
32         int numTasks = 20;
33         var state = new SharedState();
34         var tasks = new Task[numTasks];
35         for (int i = 0; i < numTasks; i  )
36         {
37             tasks[i] = new Task(new Worker(state).DoJob);
38             tasks[i].Start();
39         }
40         for (int i = 0; i < numTasks; i  )
41         {
42             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止
43         }
44         Console.WriteLine("Summarized {0}", state.State);
45     }
46 }

2、使用Paralle.ForEach()方法循环

2、使用Paralle.ForEach()方法循环

        SpinLock mSpinLock = new SpinLock(); // 最好只是用一个 SpinLock

        public void fun1()
        {
            // .....

            bool lockTaken = false;
            mSpinLock.Enter(ref lockTaken);
            try
            {
                // synchronized region
            }
            finally
            {
                mSpinLock.Exit();
            }

            // ...
        }
        public void fun2()
        {
            // .....

            bool lockTaken = false;
            mSpinLock.TryEnter(500, ref lockTaken);
            if (lockTaken)
            {
                try
                {
                    // synchronized region
                }
                finally
                {
                    mSpinLock.Exit();
                }
            }
            else
            {
                // didn't get the lock,do something else
            }

            // ...
        }

运行结果:

string[] data = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k" };
Parallel.ForEach(data, (s, pls, l) =>
{
    Console.WriteLine(s   " "   l);//s是当前循环的项的值,pls是ParallelLoopState类型,l是当前迭代的顺序
});
string[] data = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k" };
Parallel.ForEach(data, (s, pls, l) =>
{
    Console.WriteLine(s   " "   l);//s是当前循环的项的值,pls是ParallelLoopState类型,l是当前迭代的顺序
});

SpinLock结构体是 .Net 4 新增。它适用于:有大量的锁,且锁定时间都非常短。程序需要避免使用多个 SpinLock 结构,也不要调用任何可能阻塞的内容。

澳门新萄京官方网站 5

 

 

五::WaitHandle 基类

从上面结果看出,20个任务分别对共享的数据累加后,打印其结果。每个任务执行500次,共20个任务,理想的结果是10000,但是事实并非如此。事实是每次运行的结果都不同,且没有一个结果是正确的。使用lock语句时要注意的是传递的锁对象必须是引用对象,若对值对象使用lock语句,C#编译器会报错。

3、通过Parallel.Invoke()方法调用多个方法

3、通过Parallel.Invoke()方法调用多个方法

WaitHandle是一个抽象基类,用于等待一个信号的设置。可以等待不同的信号,因为WaitHandle是一个基类,可以从中派生一些类。

7、将上述代码改写为如下的SyncRoot模式,但是不能打印输出理想结果的10000。

Parallel.Invoke()方法运行传递一个Action委托数组,在其中可以指定需要并行运行的方法。

Parallel.Invoke()方法运行传递一个Action委托数组,在其中可以指定需要并行运行的方法。

        public delegate int TakesAWhileDelegate(int data, int ms); // 声明委托
        public void Main()
        {
            TakesAWhileDelegate vTAwdl = TakesAWhile;
            IAsyncResult vAr = vTAwdl.BeginInvoke(1, 3000, null, null);
            while(true)
            {
                Console.Write(".");
                if (vAr.AsyncWaitHandle.WaitOne(300, false)) // 等待 vAr.AsyncWaitHandle 收到信号(超时300毫秒)
                {
                    Console.WriteLine("Can get the result now.");
                    break;
                }
            }
            int result = vTAwdl.EndInvoke(vAr);
            Console.WriteLine("Result:{0}", result);

            Console.Read();
        }

        int TakesAWhile(int data, int ms) 
        {
            Console.WriteLine("TakesAWhile started");
            Thread.Sleep(ms);
            Console.WriteLine("TakesAWhile completed");
            return   data;
        }
 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 class SharedState
 6 {
 7     public int State { get; set; }
 8 }
 9 
10 class Worker
11 {
12     SharedState state;
13 
14     public Worker()
15     {
16         this.state = new SharedState();    
17     }
18 
19     public Worker(SharedState state)
20     {
21         this.state = state;
22     }
23 
24     public static Worker Synchronized(Worker worker)
25     {
26         if (!worker.IsSynchronized)
27         {
28             return new SynchronizedWorker(worker);
29         }
30         return worker;
31     }
32 
33     public virtual void DoJob()
34     {
35         for (int i = 0; i < 500; i  )
36         {
37             state.State  = 1;
38         }
39     }
40 
41     public virtual bool IsSynchronized
42     {
43         get { return false; }
44     }
45 
46     private class SynchronizedWorker : Worker
47     {
48         object locker = new object();
49         Worker worker;
50 
51         public SynchronizedWorker(Worker worker)
52         {
53             this.worker = worker;
54         }
55 
56         public override bool IsSynchronized
57         {
58             get { return true; }
59         }
60 
61         public override void DoJob()
62         {
63             lock (locker)
64             {
65                 worker.DoJob();
66             }
67         }
68     }
69 }
70 
71 class Program
72 {
73     static void Main(string[] args)
74     {
75         int numTasks = 20;
76         var state = new SharedState();
77         var tasks = new Task[numTasks];
78         for (int i = 0; i < numTasks; i  )
79         {
80             Worker worker = Worker.Synchronized(new Worker(state));
81             tasks[i] = new Task(worker.DoJob);
82             tasks[i].Start();
83         }
84         for (int i = 0; i < numTasks; i  )
85         {
86             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止
87         }
88         Console.WriteLine("Summarized {0}", state.State);
89     }
90 }
 1 static void Main(string[] args)
 2 {
 3     Parallel.Invoke(Say1, Say2, Say3, Say4, Say5);
 4     Console.WriteLine("---------");
 5     Say1();
 6     Say2();
 7     Say3();
 8     Say4();
 9     Say5();
10  
11     Console.ReadKey();
12 }
13 static void Say1()
14 {
15     Thread.Sleep(100);
16     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "1");
17 }
18 static void Say2()
19 {
20     Thread.Sleep(100);
21     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "2");
22 }
23 static void Say3()
24 {
25     Thread.Sleep(100);
26     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "3");
27 }
28 static void Say4()
29 {
30     Thread.Sleep(100);
31     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "4");
32 }
33 static void Say5()
34 {
35     Thread.Sleep(100);
36     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "5");
37 }
 1 static void Main(string[] args)
 2 {
 3     Parallel.Invoke(Say1, Say2, Say3, Say4, Say5);
 4     Console.WriteLine("---------");
 5     Say1();
 6     Say2();
 7     Say3();
 8     Say4();
 9     Say5();
10  
11     Console.ReadKey();
12 }
13 static void Say1()
14 {
15     Thread.Sleep(100);
16     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "1");
17 }
18 static void Say2()
19 {
20     Thread.Sleep(100);
21     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "2");
22 }
23 static void Say3()
24 {
25     Thread.Sleep(100);
26     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "3");
27 }
28 static void Say4()
29 {
30     Thread.Sleep(100);
31     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "4");
32 }
33 static void Say5()
34 {
35     Thread.Sleep(100);
36     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")   "5");
37 }

澳门新萄京官方网站 6

将SharedState类也改写为SyncRoot模式,还是不行,不明白原因。

 

 

以上实例代码,使用”异步委托", BeginInvoke() 方法返回一个实现了 IAsycResult接口的对象。使用 IAsycResult 接口,可以用AsycResult属性访问 WaitHandle 基类。在调用WaitOne()方法时,线程等待一个与等待句柄相关的信号。

 1 class SharedState
 2 {
 3     object locker = new object();
 4 
 5     int state;
 6 
 7     public int State
 8     {
 9         get
10         {
11             lock (locker)
12             {
13                 return this.state;
14             }
15         }
16         set
17         {
18             lock (locker)
19             {
20                 this.state = value;
21             }
22         }
23     }
24 }

 

 

使用 WaitHandle 类可以等待一个信号出现(WaitOne()方法)、等待必须发出信号的多个对象(WaitAll()方法)、或者等待多个对象中的一个(WaitAny()方法)。后两者事WaitHandle类的静态方法,接收一个WaitHandle参数数组。

最简单且可靠的办法是在DoJob方法中,将lock语句添加到合适的地方。

(三)任务

(三)任务

六::Mutex类

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 class SharedState
 6 {
 7     public int State { get; set; }
 8 }
 9 
10 class Worker
11 {
12     SharedState state;
13 
14     public Worker(SharedState state)
15     {
16         this.state = state;
17     }
18 
19     public void DoJob()
20     {
21         for (int i = 0; i < 500; i  )
22         {
23             // 最简单可靠的办法是在适合的地方添加lock语句
24             lock (state)
25             {
26                 state.State  = 1;
27             }
28         }
29     }
30 }
31 
32 class Program
33 {
34     static void Main(string[] args)
35     {
36         int numTasks = 20;
37         var state = new SharedState();
38         var tasks = new Task[numTasks];
39         for (int i = 0; i < numTasks; i  )
40         {
41             tasks[i] = new Task(new Worker(state).DoJob);
42             tasks[i].Start();
43         }
44         for (int i = 0; i < numTasks; i  )
45         {
46             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止
47         }
48         Console.WriteLine("Summarized {0}", state.State);
49     }
50 }

为了更好的控制并行动作,可以使用System.Threading.Tasks名称空间中的Task类。

为了更好的控制并行动作,可以使用System.Threading.Tasks名称空间中的Task类。

Mutex(mutual exclusion,互斥)是 .NET Framework中提供跨多个进程同步访问的一个类。所以,它常被用于“程序单一启动控制”。

或者也可以如下重写DoJob方法。

 

 

        /// <summary>
        /// 单一进程 检查,如果已经运行一个进程,返回false,表示检查不通过。否则返回true。
        /// </summary>
        /// <returns></returns>
        private bool RunOnceCheck()
        {
            bool vExist;
            Mutex nMutex = new Mutex(false, "SingletonWinAppMutex", out vExist);
            if (!vExist)
            {
                // 表示已经启动一个了,应退出当前启动
                return false;
            }
            return true;
        }
 1 public void DoJob()
 2 {
 3     // 最简单可靠的办法是在适合的地方添加lock语句
 4     lock (state)
 5     {
 6         for (int i = 0; i < 500; i  )
 7         {
 8             state.State  = 1;
 9         }
10     }
11 }

1、启动任务

1、启动任务

它非常类似于Monitor类,因为他们都只有一个线程能拥有锁定。只有一个线程能获得互斥锁定,访问受互斥保护的同步代码区域。Mutex派生自基类WaitHandle,因此可以利用WaitOne()方法获得互斥锁定,在该过程中成为该互斥的拥有者。调用 ReleaseMutex()方法,释放互斥。

注意:在一个地方使用lock语句并不意味着,访问对象的其他线程都正在等待。必须对每个访问共享状态的线程显示地使用同步功能。

(1)使用线程池的任务

(1)使用线程池的任务

            bool createdNew;
            Mutex mutex = new Mutex(false, "ProCSharpMutex", out createdNew);

            if (mutex.WaitOne())
            {
                try
                {
                    // synchronized region
                }
                finally
                {
                    mutex.ReleaseMutex();
                }
            }
            else
            {
                // some problem happened while waiting
            }

8、Interlocked类是一个静态类型,用于使简单的语句原子化,例如,i 不是线程安全的,它的操作包括:从内存中获取一个值,给该值递增1,再将它存储回内存。所有这些操作都有可能被线程调试器打断。

 1 private static readonly object locker = new object();
 2 static void Main(string[] args)
 3 {
 4     var tf = new TaskFactory();
 5     Task t1 = tf.StartNew(TaskMethod, "使用TaskFactory");
 6  
 7     Task t2 = Task.Factory.StartNew(TaskMethod, "使用Task.Factory");
 8  
 9     var t3 = new Task(TaskMethod, "使用Task构造函数并启动");
10     t3.Start();
11  
12     Task t4 = Task.Run(() => { TaskMethod("运行"); });
13  
14     Console.ReadKey();
15 }
16 static void TaskMethod(object title)
17 {
18     lock (locker)
19     {
20         Console.WriteLine(title);
21         Console.WriteLine("任务Id:{0},线程Id:{1}", Task.CurrentId == null ? "no Task" : Task.CurrentId.ToString(), Thread.CurrentThread.ManagedThreadId);
22         Console.WriteLine("是否为线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread);
23         Console.WriteLine("是否为后台线程:{0}",Thread.CurrentThread.IsBackground);
24         Console.WriteLine();
25     }
26 }
 1 private static readonly object locker = new object();
 2 static void Main(string[] args)
 3 {
 4     var tf = new TaskFactory();
 5     Task t1 = tf.StartNew(TaskMethod, "使用TaskFactory");
 6  
 7     Task t2 = Task.Factory.StartNew(TaskMethod, "使用Task.Factory");
 8  
 9     var t3 = new Task(TaskMethod, "使用Task构造函数并启动");
10     t3.Start();
11  
12     Task t4 = Task.Run(() => { TaskMethod("运行"); });
13  
14     Console.ReadKey();
15 }
16 static void TaskMethod(object title)
17 {
18     lock (locker)
19     {
20         Console.WriteLine(title);
21         Console.WriteLine("任务Id:{0},线程Id:{1}", Task.CurrentId == null ? "no Task" : Task.CurrentId.ToString(), Thread.CurrentThread.ManagedThreadId);
22         Console.WriteLine("是否为线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread);
23         Console.WriteLine("是否为后台线程:{0}",Thread.CurrentThread.IsBackground);
24         Console.WriteLine();
25     }
26 }

七::Semaphore类

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 class SharedState
 6 {
 7     private int state;
 8     public int State
 9     {
10         get { return this.state; }
11         set { this.state = value; }
12     }
13 
14     public void Increment()
15     {
16         Interlocked.Increment(ref state); //替代this.state  ;并且是线程安全的
17     }
18 }
19 
20 class Worker
21 {
22     SharedState state;
23 
24     public Worker(SharedState state)
25     {
26         this.state = state;
27     }
28 
29     public void DoJob()
30     {
31         for (int i = 0; i < 500; i  )
32         {
33             state.Increment();
34         }
35     }
36 }
37 
38 class Program
39 {
40     static void Main(string[] args)
41     {
42         int numTasks = 20;
43         var state = new SharedState();
44         var tasks = new Task[numTasks];
45         for (int i = 0; i < numTasks; i  )
46         {
47             tasks[i] = new Task(new Worker(state).DoJob);
48             tasks[i].Start();
49         }
50         for (int i = 0; i < numTasks; i  )
51         {
52             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止
53         }
54         Console.WriteLine("Summarized {0}", state.State);
55     }
56 }

 

 

 Semaphore非常类似于互斥,其区别在于Semaphore可以同时由多个线程使用。它是一种计数互斥锁定,可以定义允许同时访问受其锁定保护的资源的线程个数。它适用于:有许多可用资源,且只允许一定数量的线程访问该资源。

 

(2)同步任务

(2)同步任务

八::Events类

任务不一定要使用线程池中的线程,也可以使用其他线程。

任务不一定要使用线程池中的线程,也可以使用其他线程。

它是一种可以在系统范围内同步资源的方法。

TaskMethod("主线程调用");
var t1 = new Task(TaskMethod,"同步运行");
t1.RunSynchronously();
TaskMethod("主线程调用");
var t1 = new Task(TaskMethod,"同步运行");
t1.RunSynchronously();

九::Barrier类

 

 

它非常适用于其中工作有很多个任务分支且以后又需要合并工作的情况。

(3)使用单独线程的任务

(3)使用单独线程的任务

十::ReaderWriterLockSlim类

如果任务的代码应该长时间运行,就应该使用TaskCreationOptions.LongRunning告诉任务调度器创建一个新线程,而不是使用线程池中的线程。

如果任务的代码应该长时间运行,就应该使用TaskCreationOptions.LongRunning告诉任务调度器创建一个新线程,而不是使用线程池中的线程。

为了使锁定机制允许锁定多个读取器(而不是一个写入器)访问某个资源,可以使用此类。它提供了一个锁定功能,如果没有写入器锁定资源,就允许多个读取器访问资源,但只能有一个写入器锁定该资源。

var t1 = new Task(TaskMethod, "长时间运行任务", TaskCreationOptions.LongRunning);
t1.Start();
var t1 = new Task(TaskMethod, "长时间运行任务", TaskCreationOptions.LongRunning);
t1.Start();

 

 

 

 

2、Future——任务的结果

2、Future——任务的结果

 

任务结束时,它可以把一些有用的状态信息写到共享对象中,这个共享对象必须是线程安全的。另一个选项是使用返回某个结果的任务,如Future它是Task类的一个泛型版本,使用这个类时,可以定义任务返回的结果的类型。

任务结束时,它可以把一些有用的状态信息写到共享对象中,这个共享对象必须是线程安全的。另一个选项是使用返回某个结果的任务,如Future它是Task类的一个泛型版本,使用这个类时,可以定义任务返回的结果的类型。

var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(10, 5));
t1.Start();
Console.WriteLine(t1.Result);
t1.Wait();
Console.WriteLine("任务结果:{0} {1}",t1.Result.Item1, t1.Result.Item2);
var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(10, 5));
t1.Start();
Console.WriteLine(t1.Result);
t1.Wait();
Console.WriteLine("任务结果:{0} {1}",t1.Result.Item1, t1.Result.Item2);

 

 

3、连续的任务

3、连续的任务

通过任务,可以指定在任务完成后,应开始运行另一个特定任务。

通过任务,可以指定在任务完成后,应开始运行另一个特定任务。

Task t1 = new Task(DoOnFirst);
t1.Start();
Task t2 = t1.ContinueWith(DoOnSecond);
Task t3 = t1.ContinueWith(DoOnSecond);
Task t4 = t2.ContinueWith(DoOnSecond);
Task t5 = t3.ContinueWith(DoOnSecond, TaskContinuationOptions.OnlyOnFaulted);//第二个参数指t3在失败的情况下运行t5
Task t1 = new Task(DoOnFirst);
t1.Start();
Task t2 = t1.ContinueWith(DoOnSecond);
Task t3 = t1.ContinueWith(DoOnSecond);
Task t4 = t2.ContinueWith(DoOnSecond);
Task t5 = t3.ContinueWith(DoOnSecond, TaskContinuationOptions.OnlyOnFaulted);//第二个参数指t3在失败的情况下运行t5

 

 

4、任务层次结构

4、任务层次结构

任务也可以构成一个层次结构。一个任务启动一个新任务时,就启动了一个父/子层次结构。

任务也可以构成一个层次结构。一个任务启动一个新任务时,就启动了一个父/子层次结构。

 1 static void Main(string[] args)
 2 {
 3     var parent = new Task(ParentTask);
 4     parent.Start();
 5     Thread.Sleep(2000);
 6     Console.WriteLine(parent.Status);
 7     Thread.Sleep(4000);
 8     Console.WriteLine(parent.Status);
 9     Console.ReadKey();
10 }
11 static void ParentTask()
12 {
13     Console.WriteLine("任务Id:" Task.CurrentId);
14     var child = new Task(ChildTask);
15     child.Start();
16     Thread.Sleep(1000);
17     Console.WriteLine("父级子任务已开始运行");
18 }
19 static void ChildTask()
20 {
21     Console.WriteLine("子任务开始");
22     Thread.Sleep(5000);
23     Console.WriteLine("子任务结束");
24 }
 1 static void Main(string[] args)
 2 {
 3     var parent = new Task(ParentTask);
 4     parent.Start();
 5     Thread.Sleep(2000);
 6     Console.WriteLine(parent.Status);
 7     Thread.Sleep(4000);
 8     Console.WriteLine(parent.Status);
 9     Console.ReadKey();
10 }
11 static void ParentTask()
12 {
13     Console.WriteLine("任务Id:" Task.CurrentId);
14     var child = new Task(ChildTask);
15     child.Start();
16     Thread.Sleep(1000);
17     Console.WriteLine("父级子任务已开始运行");
18 }
19 static void ChildTask()
20 {
21     Console.WriteLine("子任务开始");
22     Thread.Sleep(5000);
23     Console.WriteLine("子任务结束");
24 }

 

 

 

 

(四)取消架构

(四)取消架构

.NET4.5包含一个取消架构,允许以标准方式取消长时间运行的任务。取消架构基于协作行为,它不是强制的。长时间运行的任务会检查它是否被取消,并返回控制权。支持取消的方法接受一个CancellationToken参考。

.NET4.5包含一个取消架构,允许以标准方式取消长时间运行的任务。取消架构基于协作行为,它不是强制的。长时间运行的任务会检查它是否被取消,并返回控制权。支持取消的方法接受一个CancellationToken参考。

 

 

1、Parallel.For()方法的取消

1、Parallel.For()方法的取消

 1 var cts = new CancellationTokenSource();
 2 cts.Token.Register(() => Console.WriteLine("*** token canceled"));
 3 
 4  
 5 //在500毫秒以后发送取消指令
 6 cts.CancelAfter(500);
 7 try
 8 {
 9     var result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x =>
10     {
11         Console.WriteLine("{0}次循环开始", x)
12         int sum = 0;
13         for (int i = 0; i < 100; i  )
14         {
15             Thread.Sleep(2);
16             sum  = i;
17         }
18         Console.WriteLine("{0}次循环结束", x);
19     });
20 }
21 catch (OperationCanceledException ex)
22 {
23     Console.WriteLine(ex.Message);
24 }
 1 var cts = new CancellationTokenSource();
 2 cts.Token.Register(() => Console.WriteLine("*** token canceled"));
 3 
 4  
 5 //在500毫秒以后发送取消指令
 6 cts.CancelAfter(500);
 7 try
 8 {
 9     var result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x =>
10     {
11         Console.WriteLine("{0}次循环开始", x)
12         int sum = 0;
13         for (int i = 0; i < 100; i  )
14         {
15             Thread.Sleep(2);
16             sum  = i;
17         }
18         Console.WriteLine("{0}次循环结束", x);
19     });
20 }
21 catch (OperationCanceledException ex)
22 {
23     Console.WriteLine(ex.Message);
24 }

使用.NET4.5中的一个新方法CancelAfter,在500毫秒后取消标记。在For()循环的实现代码内部,Parallel类验证CanceledToken的结果,并取消操作。一旦取消操作,For()方法就抛出一个OperationCanceledException类型的异常。

使用.NET4.5中的一个新方法CancelAfter,在500毫秒后取消标记。在For()循环的实现代码内部,Parallel类验证CanceledToken的结果,并取消操作。一旦取消操作,For()方法就抛出一个OperationCanceledException类型的异常。

 

 

2、任务的取消

2、任务的取消

同样的取消模式也可以用于任务。

同样的取消模式也可以用于任务。

 1 var cts = new CancellationTokenSource();
 2 cts.Token.Register(() => Console.WriteLine("*** token canceled"));
 3 
 4 //在500毫秒以后发送取消指令
 5 cts.CancelAfter(500);
 6  
 7 Task t1 = Task.Run(()=> {
 8     Console.WriteLine("任务进行中...");
 9     for (int i = 0; i < 20; i  )
10     {
11         Thread.Sleep(100);
12         CancellationToken token = cts.Token;
13         if (token.IsCancellationRequested)
14         {
15             Console.WriteLine("已发送取消请求,取消请求来自当前任务");
16             token.ThrowIfCancellationRequested();
17             break;
18         }
19         Console.WriteLine("循环中...");
20     }
21     Console.WriteLine("任务结束没有取消");
22 });
23 try
24 {
25     t1.Wait();
26 }
27 catch (AggregateException ex)
28 {
29     Console.WriteLine("异常:{0}, {1}",ex.GetType().Name,ex.Message);
30     foreach (var innerException in ex.InnerExceptions)
31     {
32         Console.WriteLine("异常:{0}, {1}", ex.InnerException.GetType().Name, ex.InnerException.Message);
33     }
34 }
 1 var cts = new CancellationTokenSource();
 2 cts.Token.Register(() => Console.WriteLine("*** token canceled"));
 3 
 4 //在500毫秒以后发送取消指令
 5 cts.CancelAfter(500);
 6  
 7 Task t1 = Task.Run(()=> {
 8     Console.WriteLine("任务进行中...");
 9     for (int i = 0; i < 20; i  )
10     {
11         Thread.Sleep(100);
12         CancellationToken token = cts.Token;
13         if (token.IsCancellationRequested)
14         {
15             Console.WriteLine("已发送取消请求,取消请求来自当前任务");
16             token.ThrowIfCancellationRequested();
17             break;
18         }
19         Console.WriteLine("循环中...");
20     }
21     Console.WriteLine("任务结束没有取消");
22 });
23 try
24 {
25     t1.Wait();
26 }
27 catch (AggregateException ex)
28 {
29     Console.WriteLine("异常:{0}, {1}",ex.GetType().Name,ex.Message);
30     foreach (var innerException in ex.InnerExceptions)
31     {
32         Console.WriteLine("异常:{0}, {1}", ex.InnerException.GetType().Name, ex.InnerException.Message);
33     }
34 }

 

 

 

 

(五)线程池

(五)线程池

int nWorkerThreads;
int nCompletionPortThreads;
ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads);
Console.WriteLine("线程池中辅助线程的最大数目:{0}, 线程池中异步 I/O 线程的最大数目:{1}",nWorkerThreads,nCompletionPortThreads);
for (int i = 0; i < 5; i  )
{
    ThreadPool.QueueUserWorkItem(JobForAThread);
}
Thread.Sleep(3000);
int nWorkerThreads;
int nCompletionPortThreads;
ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads);
Console.WriteLine("线程池中辅助线程的最大数目:{0}, 线程池中异步 I/O 线程的最大数目:{1}",nWorkerThreads,nCompletionPortThreads);
for (int i = 0; i < 5; i  )
{
    ThreadPool.QueueUserWorkItem(JobForAThread);
}
Thread.Sleep(3000);

需要注意的是:线程池中的所有线程都是后台线程,不可设置线程优先级、名称,随着前台线程的结束而结束,只适用于短时间任务。

需要注意的是:线程池中的所有线程都是后台线程,不可设置线程优先级、名称,随着前台线程的结束而结束,只适用于短时间任务。

 

 

(六)Thread类

(六)Thread类

该类允许创建前台线程,以及设置线程的优先级。

该类允许创建前台线程,以及设置线程的优先级。

 

 

1、给线程传递数据

1、给线程传递数据

static void Main(string[] args)
{
    var t2 = new Thread(ThreadMainWithParameter);
    t2.Start("参数字符串");

    Console.ReadKey();
}
static void ThreadMainWithParameter(object message)
{
    Console.WriteLine("运行主线程,接受参数:"   message.ToString());
}
static void Main(string[] args)
{
    var t2 = new Thread(ThreadMainWithParameter);
    t2.Start("参数字符串");

    Console.ReadKey();
}
static void ThreadMainWithParameter(object message)
{
    Console.WriteLine("运行主线程,接受参数:"   message.ToString());
}

如果使用了ParameterizedThreadStart委托,线程的入口点必须有一个object类型的参数,且返回类型为void。

如果使用了ParameterizedThreadStart委托,线程的入口点必须有一个object类型的参数,且返回类型为void。

 

 

2、后台线程

2、后台线程

只要有一个前台线程在运行,应用程序的进程就在运行。如果多个前台线程在运行,而Main()方法结束了,应用程序的进程就仍然是激活的,直到所有前台线程完成其任务为止。

只要有一个前台线程在运行,应用程序的进程就在运行。如果多个前台线程在运行,而Main()方法结束了,应用程序的进程就仍然是激活的,直到所有前台线程完成其任务为止。

默认情况下,用Thread类创建的线程是前台线程,线程池中的线程是后台线程。Thread类创建线程时,可以设置IsBackground属性来确定创建前台还是后台线程。

默认情况下,用Thread类创建的线程是前台线程,线程池中的线程是后台线程。Thread类创建线程时,可以设置IsBackground属性来确定创建前台还是后台线程。

static void Main(string[] args)
{
    var t1 = new Thread(ThreadMain) { Name = "MyNewThread", IsBackground = false };
    t1.Start();
    Console.WriteLine("主线程现在结束");
    Console.ReadKey();
}


private static void ThreadMain()
{
    Console.WriteLine(Thread.CurrentThread.Name "线程开始运行");
    Thread.Sleep(3000);
    Console.WriteLine(Thread.CurrentThread.Name "线程结束");
}
static void Main(string[] args)
{
    var t1 = new Thread(ThreadMain) { Name = "MyNewThread", IsBackground = false };
    t1.Start();
    Console.WriteLine("主线程现在结束");
    Console.ReadKey();
}


private static void ThreadMain()
{
    Console.WriteLine(Thread.CurrentThread.Name "线程开始运行");
    Thread.Sleep(3000);
    Console.WriteLine(Thread.CurrentThread.Name "线程结束");
}

在通过Thread类创建线程的时候,设置IsBackground属性为false,也就是创建一个前台线程。这种情况下在主线程结束时,t1不会结束。但如果将IsBackground设置为true,则会随着主线程的结束而结束。

在通过Thread类创建线程的时候,设置IsBackground属性为false,也就是创建一个前台线程。这种情况下在主线程结束时,t1不会结束。但如果将IsBackground设置为true,则会随着主线程的结束而结束。

 

 

3、线程的优先级

3、线程的优先级

给线程指定优先级,就可以影响系统对线程的调度顺序。在Thread类中,可以设置Priority属性,以影响线程的基本优先级。

给线程指定优先级,就可以影响系统对线程的调度顺序。在Thread类中,可以设置Priority属性,以影响线程的基本优先级。

 

 

4、控制线程

4、控制线程

读取Tread的ThreadState属性,可以获取线程的状态。

读取Tread的ThreadState属性,可以获取线程的状态。

Thread的Start()方法创建线程,线程状态为UnStarted;

Thread的Start()方法创建线程,线程状态为UnStarted;

线程调度器选择运行后,线程转台改变为Running;

线程调度器选择运行后,线程转台改变为Running;

Thread.Sleep()方法会使线程处于WaitSleepJoin;

Thread.Sleep()方法会使线程处于WaitSleepJoin;

Thread类的Abort()方法,触发ThreadAbortException类型的异常,线程状态会变为AbortedRequested,如果没有重置终止则变为Aborted;

Thread类的Abort()方法,触发ThreadAbortException类型的异常,线程状态会变为AbortedRequested,如果没有重置终止则变为Aborted;

Thread.ResetAbort()方法可以让线程在触发ThreadAbortException异常后继续运行;

Thread.ResetAbort()方法可以让线程在触发ThreadAbortException异常后继续运行;

Thread类的Join()会停止当前线程,等待加入的线程完成为止,此时线程状态为WaitSleepJoin。

Thread类的Join()会停止当前线程,等待加入的线程完成为止,此时线程状态为WaitSleepJoin。

 

 

 

 

(七)线程问题

(七)线程问题

1、争用条件

1、争用条件

如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件。要避免该问题,可以锁定共享对象。这可以通过lock语句锁定在线程中共享的state变量。

如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件。要避免该问题,可以锁定共享对象。这可以通过lock语句锁定在线程中共享的state变量。

private static readonly object locker = new object();

public void ChnageI(int i)
{
    lock (locker)
    {
        if (i == 0)
        {
            i  ;
            Console.WriteLine(i == 1);
        }
        i = 0;
    }
}
private static readonly object locker = new object();

public void ChnageI(int i)
{
    lock (locker)
    {
        if (i == 0)
        {
            i  ;
            Console.WriteLine(i == 1);
        }
        i = 0;
    }
}

 

 

2、死锁

2、死锁

由于两个线程都在等待对方,就出现了死锁,线程将无线等待下去。为了避免这个问题,可以在应用程序的体系架构中,从一开始就设计好锁定的顺序,也可以为锁定定义超时时间。

由于两个线程都在等待对方,就出现了死锁,线程将无线等待下去。为了避免这个问题,可以在应用程序的体系架构中,从一开始就设计好锁定的顺序,也可以为锁定定义超时时间。

 

 

 

 

(八)同步

(八)同步

共享数据必须使用同步技术,确保一次只有一个线程访问和改变共享状态。可以使用lock语句、Interlocked类和Monitor类进行进程内部的同步。Mutex类、Event类、SemaphoreSlim类和ReaderWriterLockSlim类提供了多个进程之间的线程同步。

共享数据必须使用同步技术,确保一次只有一个线程访问和改变共享状态。可以使用lock语句、Interlocked类和Monitor类进行进程内部的同步。Mutex类、Event类、SemaphoreSlim类和ReaderWriterLockSlim类提供了多个进程之间的线程同步。

 

 

1、lock语句和线程安全

1、lock语句和线程安全

lock语句是设置锁定和解除锁定的一种简单方式。

lock语句是设置锁定和解除锁定的一种简单方式。

在没有使用lock语句的情况下,多个线程操作共享数据,最后得到的结果没有一个会正确。

在没有使用lock语句的情况下,多个线程操作共享数据,最后得到的结果没有一个会正确。

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         for (int j = 0; j < 5; j  )
 6         {
 7             int numTasks = 20;
 8             var state = new SharedState();
 9             var tasks = new Task[numTasks];
10             for (int i = 0; i < numTasks; i  )
11             {
12                 tasks[i] = Task.Run(() => { new Job(state).DoTheJob(); });
13             }
14  
15             for (int i = 0; i < numTasks; i  )
16             {
17                 tasks[i].Wait();
18             }
19             Console.WriteLine("最后结果:{0}", state.State);
20         }
21         Console.ReadKey();
22     }
23 }
24 
25 public class SharedState
26 {
27     public int State { get; set; }
28 }
29 
30 public class Job
31 {
32     SharedState sharedState;
33     public Job(SharedState sharedState)
34     {
35         this.sharedState = sharedState;
36     }
37     public void DoTheJob()
38     {
39         for (int i = 0; i < 50000; i  )
40         {
41             sharedState.State  = 1;
42         }
43     }
44 }
 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         for (int j = 0; j < 5; j  )
 6         {
 7             int numTasks = 20;
 8             var state = new SharedState();
 9             var tasks = new Task[numTasks];
10             for (int i = 0; i < numTasks; i  )
11             {
12                 tasks[i] = Task.Run(() => { new Job(state).DoTheJob(); });
13             }
14  
15             for (int i = 0; i < numTasks; i  )
16             {
17                 tasks[i].Wait();
18             }
19             Console.WriteLine("最后结果:{0}", state.State);
20         }
21         Console.ReadKey();
22     }
23 }
24 
25 public class SharedState
26 {
27     public int State { get; set; }
28 }
29 
30 public class Job
31 {
32     SharedState sharedState;
33     public Job(SharedState sharedState)
34     {
35         this.sharedState = sharedState;
36     }
37     public void DoTheJob()
38     {
39         for (int i = 0; i < 50000; i  )
40         {
41             sharedState.State  = 1;
42         }
43     }
44 }

使用lock语句,修改DoTheJob()方法,现在才能获得正确的结果。

使用lock语句,修改DoTheJob()方法,现在才能获得正确的结果。

private readonly object syncRoot = new object();

public void DoTheJob()
{
    for (int i = 0; i < 50000; i  )
    {
        lock (syncRoot)
        {
            sharedState.State  = 1;
        }
    }
}
private readonly object syncRoot = new object();

public void DoTheJob()
{
    for (int i = 0; i < 50000; i  )
    {
        lock (syncRoot)
        {
            sharedState.State  = 1;
        }
    }
}

 

 

2、Interlocked类

2、Interlocked类

Interlocked类用于使变量的简单语句原子化。

Interlocked类用于使变量的简单语句原子化。

public int State
{
    get
    {
        lock (this)
        {
            return   state;
        }
    }
}

public int State
{
    get
    {
        return Interlocked.Increment(ref state);
    }
}
public int State
{
    get
    {
        lock (this)
        {
            return   state;
        }
    }
}

public int State
{
    get
    {
        return Interlocked.Increment(ref state);
    }
}

使用Interlocked类可以更快。

使用Interlocked类可以更快。

 

 

3、Monitor类

3、Monitor类

lock语句由C#编译器解析为使用Monitor类。

lock语句由C#编译器解析为使用Monitor类。

lock (syncRoot)
{
    //代码
}
//C#编译器将lock语句解析为
Monitor.Enter(syncRoot);
try
{
    //代码
}
finally
{
    Monitor.Exit(syncRoot);
}
lock (syncRoot)
{
    //代码
}
//C#编译器将lock语句解析为
Monitor.Enter(syncRoot);
try
{
    //代码
}
finally
{
    Monitor.Exit(syncRoot);
}

Monitor类相对于lock语句的优点是:可以通过调用TryEnter()方法添加一个等待被锁定的超时值。

Monitor类相对于lock语句的优点是:可以通过调用TryEnter()方法添加一个等待被锁定的超时值。

bool lockTaken = false;
Monitor.TryEnter(syncRoot, 1000, ref lockTaken);
if (lockTaken)
{
    //获取锁后操作
    try
    {
        //代码
    }
    finally
    {
        Monitor.Exit(syncRoot);
    }
}
else
{
    //没有获取锁的操作
}
bool lockTaken = false;
Monitor.TryEnter(syncRoot, 1000, ref lockTaken);
if (lockTaken)
{
    //获取锁后操作
    try
    {
        //代码
    }
    finally
    {
        Monitor.Exit(syncRoot);
    }
}
else
{
    //没有获取锁的操作
}

 

 

4、SpinLock结构

4、SpinLock结构

相对于Monitor垃圾回收导致过高的系统开销,使用SpinLock结构就能有效降低系统开销。SpinLock的使用方式与Monitor非常相似,但因为SpinLock是结构所以在把变量赋值为另一个变量会创建一个副本。

相对于Monitor垃圾回收导致过高的系统开销,使用SpinLock结构就能有效降低系统开销。SpinLock的使用方式与Monitor非常相似,但因为SpinLock是结构所以在把变量赋值为另一个变量会创建一个副本。

 

 

5、WaitHandle基类

5、WaitHandle基类

WaitHandle是一个抽象基类,用于等待一个信号的设置。可以等待不同的信号,因为WaitHandle是一个基类,可以从中派生一些类。

WaitHandle是一个抽象基类,用于等待一个信号的设置。可以等待不同的信号,因为WaitHandle是一个基类,可以从中派生一些类。

 

 

6、Mutex类

6、Mutex类

Mutex(mutual exclusion,互斥)是.NET Framework中提供跨多个线程同步访问的一个类。

Mutex(mutual exclusion,互斥)是.NET Framework中提供跨多个线程同步访问的一个类。

在Mutex类的构造函数中,可以指定互斥是否最初由主调线程拥有,定义互斥的名称,获得互斥是否存在的信息。

在Mutex类的构造函数中,可以指定互斥是否最初由主调线程拥有,定义互斥的名称,获得互斥是否存在的信息。

bool createdNew;
var mutex = new Mutex(false, "MyMutex", out createdNew);
bool createdNew;
var mutex = new Mutex(false, "MyMutex", out createdNew);

系统可以识别有名称的互斥,可以使用它来禁止应用程序启动两次。

系统可以识别有名称的互斥,可以使用它来禁止应用程序启动两次。

bool createdNew;
var mutex = new Mutex(false, "MyMutex", out createdNew);
if (!createdNew)
{
    Console.WriteLine("每次只能启动一个应用程序");
    Environment.Exit(0);
}
Console.WriteLine("运行中...");
bool createdNew;
var mutex = new Mutex(false, "MyMutex", out createdNew);
if (!createdNew)
{
    Console.WriteLine("每次只能启动一个应用程序");
    Environment.Exit(0);
}
Console.WriteLine("运行中...");

 

 

7、Semaphore类

7、Semaphore类

信号量是一种计数的互斥锁。如果需要限制可以访问可用资源的线程数,信号量就很有用。

信号量是一种计数的互斥锁。如果需要限制可以访问可用资源的线程数,信号量就很有用。

.NET4.5为信号量功能提供了两个类Semaphore和SemaphoreSlim。Semaphore类可以命名,使用系统范围内的资源,允许在不同进程之间同步。SemaphoreSlim类是对较短等待时间进行了优化的轻型版本。

.NET4.5为信号量功能提供了两个类Semaphore和SemaphoreSlim。Semaphore类可以命名,使用系统范围内的资源,允许在不同进程之间同步。SemaphoreSlim类是对较短等待时间进行了优化的轻型版本。

 1 static void Main(string[] args)
 2 {
 3     int taskCount = 6;
 4     int semaphoreCount = 3;
 5     var semaphore = new SemaphoreSlim(semaphoreCount, semaphoreCount);
 6     var tasks = new Task[taskCount];
 7 
 8  
 9     for (int i = 0; i < taskCount; i  )
10     {
11         tasks[i] = Task.Run(() =>
12         {
13             TaskMain(semaphore);
14         });
15     }
16 
17     Task.WaitAll(tasks);
18  
19     Console.WriteLine("所有任务已结束");
20     Console.ReadKey();
21 }
22 
23  
24 private static void TaskMain(SemaphoreSlim semaphore)
25 {
26     bool isCompleted = false;
27     while (!isCompleted)
28     {
29         if (semaphore.Wait(600))
30         {
31             try
32             {
33                 Console.WriteLine("任务{0}锁定了信号", Task.CurrentId);
34                 Thread.Sleep(2000);
35             }
36             finally
37             {
38                 Console.WriteLine("任务{0}释放了信号", Task.CurrentId);
39                 semaphore.Release();
40                 isCompleted = true;
41             }
42         }
43         else
44         {
45             Console.WriteLine("任务{0}超时,等待再次执行", Task.CurrentId);
46         }
47     }
48 }
 1 static void Main(string[] args)
 2 {
 3     int taskCount = 6;
 4     int semaphoreCount = 3;
 5     var semaphore = new SemaphoreSlim(semaphoreCount, semaphoreCount);
 6     var tasks = new Task[taskCount];
 7 
 8  
 9     for (int i = 0; i < taskCount; i  )
10     {
11         tasks[i] = Task.Run(() =>
12         {
13             TaskMain(semaphore);
14         });
15     }
16 
17     Task.WaitAll(tasks);
18  
19     Console.WriteLine("所有任务已结束");
20     Console.ReadKey();
21 }
22 
23  
24 private static void TaskMain(SemaphoreSlim semaphore)
25 {
26     bool isCompleted = false;
27     while (!isCompleted)
28     {
29         if (semaphore.Wait(600))
30         {
31             try
32             {
33                 Console.WriteLine("任务{0}锁定了信号", Task.CurrentId);
34                 Thread.Sleep(2000);
35             }
36             finally
37             {
38                 Console.WriteLine("任务{0}释放了信号", Task.CurrentId);
39                 semaphore.Release();
40                 isCompleted = true;
41             }
42         }
43         else
44         {
45             Console.WriteLine("任务{0}超时,等待再次执行", Task.CurrentId);
46         }
47     }
48 }

 

 

8、Events类

8、Events类

与互斥和信号量对象一样,事件也是一个系统范围内的资源同步方法。为了从托管代码中使用系统事件,.NET Framework在System.Threading名称空间中提供了ManualResetEvent、AutoResetEvent、ManualResetEventSlim和CountdownEvent类。

与互斥和信号量对象一样,事件也是一个系统范围内的资源同步方法。为了从托管代码中使用系统事件,.NET Framework在System.Threading名称空间中提供了ManualResetEvent、AutoResetEvent、ManualResetEventSlim和CountdownEvent类。

C#中event关键字与这里的event类没有任何关系。

C#中event关键字与这里的event类没有任何关系。

 

 

9、Barrier类

9、Barrier类

对于同步,Barrier类非常适用于其中工作有多个任务分支且以后又需要合并工作的情况。

对于同步,Barrier类非常适用于其中工作有多个任务分支且以后又需要合并工作的情况。

 

 

10、ReaderWriterLockSlim类

10、ReaderWriterLockSlim类

为了使锁定机制允许锁定多个读取器访问某个资源,可以使用ReaderWriterLockSlim类。

为了使锁定机制允许锁定多个读取器访问某个资源,可以使用ReaderWriterLockSlim类。

 

 

(九)Timer类

(九)Timer类

.NET Framework提供了几个Timer类,用于在某个时间间隔后调用某个方法。System.Threading.Timer、System.Timers.Timer、System.WIndows.Forms.Timer、System.Web.UI.Timer和System.Windows.Threading.Timer。

.NET Framework提供了几个Timer类,用于在某个时间间隔后调用某个方法。System.Threading.Timer、System.Timers.Timer、System.WIndows.Forms.Timer、System.Web.UI.Timer和System.Windows.Threading.Timer。

 

 

(十)数据流

(十)数据流

Parallel类、Task类和Parallel LINQ为数据并行性提供了很多帮助。但是,这些类不能直接支持数据流的处理,以及并行转换数据。这种情况下,使用System.Threading.Tasks.Dataflow名称空间中的相关类来处理。

Parallel类、Task类和Parallel LINQ为数据并行性提供了很多帮助。但是,这些类不能直接支持数据流的处理,以及并行转换数据。这种情况下,使用System.Threading.Tasks.Dataflow名称空间中的相关类来处理。

本文由澳门新萄京官方网站发布于www.8455.com,转载请注明出处:澳门新萄京官方网站:高级编程,进程内部的同

关键词: