(29)C#多线程

使用线程的原因

1.不希望用户界面停止响应。

2.所有需要等待的操作,如文件、数据库或网络访问需要一定的时间。

一个进程的多个线程可以同时运行不同cpu或多核cpu的不同内核上

注意多线程访问相同的数据必须实现同步机制

 

编写能够利用并行性的代码需要区分两种场景:任务并行性和数据并行性

任务并行性:使用CPU的代码被并行化,利用cpu的多个核心快速的完成包含多个任务的活动。

数据并行性:使用了数据集合,在集合上执行的工作被划分为多个任务。

任务并行性和数据并行性可以混合起来。

 

Parallel类(自 4.0 起可用)

Parallel类定义了并行的for和foreach的静态方法,使用多线程来完成作业。

Parallel.For()和Parallel.ForEach()方法再每次迭代中调用相同的代码。而Parallel.Invoke()方法允许同时调用不同的方法。

Parallel.Invoke()用于任务并行性,Parallel.Invoke()用于数据并行性。

 

Parallel.For()

 

using System.Threading;
using System.Threading.Tasks;

 

    class Program
    {     
        static void Main(string[] args)
        {
 
            //参数1 int ,参数2 int,参数3 action<int>
            Parallel.For(3, 10, (i)=> {
                Console.WriteLine("id:" + i + "   thread:" + Thread.CurrentThread.ManagedThreadId);
            });
            Console.WriteLine("end");
            Console.ReadLine();
        }
    }

 

参数3的action中可以添加一个控制状态的类型

Action<Int32,ParallelLoopState>)

使用 Break()和Stop()尽早的结束循环,在结束前启动的线程仍可以继续执行。

Break() 尽早结束当前以外的线程

Stop() 尽早结束

    class Program
    {     
        static void Main(string[] args)
        {
            
            Parallel.For(3, 100000, (i, state) => {
                if (i > 6)
                {
                    state.Stop();
                    Console.WriteLine("abc");
                }
                Console.WriteLine("id:" + i + "   thread:" + Thread.CurrentThread.ManagedThreadId);
            });
            Console.WriteLine("end");
            Console.ReadLine();
        }
    }

 

Parallel.ForEach()

用来遍历一个能被迭代的集合,相当于一个多线程的foreach版

    class Program
    {     
        static void Main(string[] args)
        {
            int[] count = { 2, 3, 6, 4, 7, 25, 42, 612, 12 };
            Parallel.ForEach(count, (i, state) => {
   
                Console.WriteLine("id:" + i + "   thread:" + Thread.CurrentThread.ManagedThreadId);
            });
            Console.WriteLine("end");
            Console.ReadLine();
        }
    }

 

Parallel类返回类型

Parallel.for 和 Parallel.foreach 返回 ParallelLoopResult 类型,用来判断是否执行完成
    class Program
    {
        static void Main(string[] args)
        {
            ParallelLoopResult res = Parallel.For(3, 10, (i, state) => {
                Console.WriteLine("id:" + i + "   thread:" + Thread.CurrentThread.ManagedThreadId);
            });
            Console.WriteLine(res.IsCompleted);
            Console.ReadLine();
        }
    }

 

 

 

Parallel.Invoke()

public static void Invoke (params Action[] actions) ,可以调用多个不同的方法

 

    class Program
    {
        static void Main(string[] args)
        {
            Parallel.Invoke(a,b);
            Console.WriteLine("end");
            Console.ReadLine();
        }

        static public void a()
        {
            Console.WriteLine("a");
        }
        static public void b()
        {
            Console.WriteLine("b");
        }
    }

 

 任务

 

 1.工厂类的方式启动一个线程

        static void Main(string[] args)
        {
            var tf = new TaskFactory();
            Task task = tf.StartNew(abc);
            Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

        static public void abc()
        {
            Console.WriteLine("abc:"+ Thread.CurrentThread.ManagedThreadId);
        }

 

 

 

2.Task的静态Factory属性启动线程

 

        static void Main(string[] args)
        {
    
            Task task = Task.Factory.StartNew(abc);
            Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

 

3.使用Task的构造函数

 实例化后不会立即启动线程,当对象调用Start()方法时才开始启动

        static void Main(string[] args)
        {
    
            Task task =new Task(abc);
            task.Start();
            Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

 

 4.使用Task的静态Run方法

        static void Main(string[] args)
        {
            Task task =Task.Run(abc);
            Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

 

5.使用Task同步运行

        static void Main(string[] args)
        {
            Task task =new Task(abc);
            task.RunSynchronously();
            Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

 

6.使用单独的线程,而不是线程池

        static void Main(string[] args)
        {
            Task task =new Task(abc,TaskCreationOptions.LongRunning);
            task.Start();
            Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

 

7.带返回值的任务

  Func<>   返回Task<TResult>

        static void Main(string[] args)
        {
            Task<int> task =Task<int>.Run(()=>{
                Console.WriteLine("111");
                return 5;
            });
            Console.WriteLine(task.Result);
            Console.WriteLine("abc");
            Console.ReadLine();
        }

task.Result会等到Task执行完才会被调用相当于调用的wait,所以abc会在结果打印之后再打印

 

8.连续的任务

 ContinueWith

        static void Main(string[] args)
        {
            Task task1 = new Task(a);
            Task task2 = task1.ContinueWith(b);
            Task task3 = task2.ContinueWith(c);
            task1.Start();
            Console.WriteLine("end");
            Console.ReadLine();
        }

        static public void a()
        {
            Console.WriteLine("a:" + Thread.CurrentThread.ManagedThreadId);
        }

        static public void b(Task t)
        {
            Console.WriteLine("b:" + Thread.CurrentThread.ManagedThreadId);
        }

        static public void c(Task t)
        {
            Console.WriteLine("c:" + Thread.CurrentThread.ManagedThreadId);
        }

 

 

 

9.连续任务控制

使用TaskContinuationOptions的枚举成员,上一步出现某种问题时,来确定是否执行现在的方法

        static void Main(string[] args)
        {
            Task task1 = new Task(a);
            Task task2 = task1.ContinueWith(b,TaskContinuationOptions.DenyChildAttach);
            task1.Start();
            Console.WriteLine("end");
            Console.ReadLine();
        }

 

 10.任务的层次

 

11.

 

 

 ----------------------------------

Thread类

    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(M);
            t1.Start();
            //Thread.Sleep(1000);
            Console.WriteLine("BBB");
            Console.ReadKey();
        }

        static void M()
        {
           
            Console.WriteLine("AAA");
        }
    }

先输出AAA,还是BBB取决于操作系统调度

 

委托方式,输出结果和上列子相同

    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => Console.WriteLine("AAA"));
            t1.Start();
            Thread.Sleep(1000);
            Console.WriteLine("BBB");
            Console.ReadKey();
        }
    }

 

给线程传递数据

 方法一,启动线程时传递参数

    class Program
    {
        static void Main(string[] args)
        {
            C c = new C { Message="hello" };
            Thread t = new Thread(ThreadM);
            t.Start(c);//线程启动时传参
            Console.ReadKey();
        }

        static void ThreadM(object O)
        {
            C c = (C)O;
            Console.WriteLine("AAA:{0}",c.Message);
        }
    }
    public class C
    {
        public string Message;
    }

方法二、自定义类

    class Program
    {
        static void Main(string[] args)
        {
            C c = new C ("hello");
            Thread t = new Thread(c.ThreadMain);
            t.Start();
            Console.ReadKey();
        }
    }
    public class C
    {
        public string Message;
        //构造函数
        public C(string Message)
        {
            this.Message = Message;
        }

        public void ThreadMain()
        {
            Console.WriteLine("AAA:{0}",Message);
        }
    }

 

后台线程

前台线程可以有多个,后台线程也可以有多个。只要有一个前台线程在运行,程序的Main方法就不算结束。

Thread类创建的是前台线程。线城池中的线程时后台线程。

        static void Main(string[] args)
        {
            //IsBackground默认为false,表示为前台线程
            Thread t = new Thread(ThreadMain) { Name="线程",IsBackground=false};
            t.Start();
            Console.WriteLine("AAA");
            Console.ReadKey();
        }


        public static void ThreadMain()
        {
            Console.WriteLine("{0}启动",Thread.CurrentThread.Name);
            Thread.Sleep(6000);
        }
    }

如果IsBackground=false,按任意键后,会等执行完sleep方法才会结束。

如果IsBackground=true,按任意键后会立即结束。

这说明,如果有一个前台线程没执行完,Main方法就算都执行完,也要等到前台程序执行完才算是结束。

 

线程优先级

 线程的priority属性可以控制线程的优先级

它是一个枚举类型

Highest > AboveNormal > Normal > BelowNormal > Lowest

    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(ThreadMain) { Name = "A", Priority = ThreadPriority.Lowest };
            Thread t2 = new Thread(ThreadMain) { Name = "B", Priority = ThreadPriority.Highest };
            Thread t3 = new Thread(ThreadMain) { Name = "C", Priority = ThreadPriority.Normal };
            t1.Start();
            t2.Start();
            t3.Start();
            Console.ReadKey();
        }
        public static void ThreadMain()
        {
            Thread.Sleep(1000);
            for (int i = 0; i < 5000; i++)
            {
                Console.Write("{0}", Thread.CurrentThread.Name);
            }
        }
    }

线程优先级高的占用的CPU会更多一些

线程问题

争用条件

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

解决办法:加一个lock锁,只能锁引用类型

    class Program
    {
      static  object o = new object();
        static void Main(string[] args)
        {

        }
        public static void a()
        {
            lock (Program.o)
            {
                .....
            }
        }
    }

死锁

锁定过多可能会引发死锁问题

同步

 

posted @ 2017-03-12 19:38  富坚老贼  阅读(192)  评论(0编辑  收藏  举报