• 总结:任务、线程和同步


    近期又看了一遍《C#高级编程》这本书,想对书中——任务、线程和同步这一章知识点做一个笔记,让以后工作中忘记某个知识点能直接拿来用,在此进行一个总结。

    Parallel数据和任务并行

    一、Parallel.For

    1、用Parallel.For并行运行迭代

    static void ParallelFor()
    {
        ParallelLoopResult result = Parallel.For(0, 10, item =>
        {
            Console.WriteLine("{0},任务{1},线程{2}",
                item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(10);
        });
        Console.WriteLine("完成:{0}", result.IsCompleted);
    }

    2、利用ParallelLoopState的Break()方法提前停止For循环

    static void ParallelForBreak()
    {
        ParallelLoopResult result = Parallel.For(0, 10, (item, pls) =>
        {
            Console.WriteLine("{0},任务{1},线程{2}",
                item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(10);
            if (item > 5) pls.Break();
        });
        Console.WriteLine("完成:{0}", result.IsCompleted);
        Console.WriteLine("忽略:{0}", result.LowestBreakIteration);
    }

    3、对每个线程进行初始化

    static void ParallelForInit()
    {
        ParallelLoopResult result = Parallel.For<string>(0, 20,
            () =>
            //Func<TLocal>仅对用于执行迭代的每个线程调用一次
            {
                Console.WriteLine("初始化,任务:{0},线程:{1}",
                Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                return string.Format("线程:{0}", Thread.CurrentThread.ManagedThreadId);
            },
            (item, pls, str1) =>
            //Func<long, ParallelLoopState, TLocal, TLocal> 为循环体定义的委托
            //第一个参数是循环迭代,第二个参数ParallelLoopState允许停止循环
            //第三个参数接收初始化任务返回的值,类型是泛型For参数定义的
            {
                Console.WriteLine("执行中,编号:{0},str1:{1},任务:{2},线程:{3}",
                    item, str1, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
                return string.Format("编号:{0}", item);
            },
            (str1) =>
            //Action<TLocal> 这个方法仅对于每个线程调用一次,这是一个线程退出方法
            {
                Console.WriteLine("完成:{0}", str1);
            });
    }

    4、取消任务

    /// <summary>
    /// 取消任务
    /// </summary>
    static void ParallelCancel()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        //注册一个任务取消完成时的委托
        cts.Token.Register(() =>
        {
            Console.WriteLine("任务已取消");
        });
        try
        {
            ParallelLoopResult result = Parallel.For(0, 10,
                new ParallelOptions()
                {
                    CancellationToken = cts.Token
                },
                item =>
                {
                    Console.WriteLine("循环:{0}", item);
                    Thread.Sleep(1000);
                    if (item == 5) cts.Cancel();
                });
        }
        catch (OperationCanceledException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    二、Parallel.ForEach

    1、用Parallel.ForEach进行异步遍历

    static void ParallelForeach()
    {
        string[] data = { "张三", "李四", "王五", "赵六", "钱七" };
        ParallelLoopResult result = Parallel.ForEach(data, item =>
        {
            Console.WriteLine("值:{0},任务:{1},线程:{2}",
                item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
        });
    }

    2、利用ParallelLoopState的Break()方法中断ForEach遍历

    static void ParallelForeachBreak()
    {
        string[] data = { "张三", "李四", "王五", "赵六", "钱七" };
        ParallelLoopResult result = Parallel.ForEach(data, (item, pls, index) =>
        {
            Console.WriteLine("值:{0},索引:{1},任务:{2},线程:{3}",
                item, index, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
            if (item == "王五") pls.Break();
        });
    }

    三、Parallel.Invoke

    1、通过Parallel.Invoke()方法调用多个方法,如果多个任务应并行运行,就可以使用Parallel.Invoke方法,它提供了任务并行性模式

    static void ParallelInvoke()
    {
        Parallel.Invoke(Zhangsan, Lisi);
    }
    static void Zhangsan()
    {
        Console.WriteLine("张三");
    }
    static void Lisi()
    {
        Console.WriteLine("李四");
    }

    Task任务

    一、创建一个任务执行的方法。

    static object taskMethodLock = new object();
    static void TaskMethod(object title)
    {
        lock (taskMethodLock)
        {
            Console.WriteLine(title);
            Console.WriteLine("任务:{0},线程:{1}",
                Task.CurrentId == null ? -1 : Task.CurrentId,
                Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("线程池:{0}", Thread.CurrentThread.IsThreadPoolThread);
            Console.WriteLine("后台线程:{0}", Thread.CurrentThread.IsBackground);
            Console.WriteLine();
        }
    }

    二、用不同方式创建任务

    /// <summary>
    /// 用不同方式创建任务
    /// </summary>
    static void CreateTask()
    {
        //方式1:使用实例化的TaskFactory类
        TaskFactory tf = new TaskFactory();
        Task task = tf.StartNew(TaskMethod, "使用TaskFactory类开始任务");
        //方式2:使用Task类的静态属性Factory
        Task.Factory.StartNew(TaskMethod, "使用Task类的静态属性Factory开始任务");
        //方式3:使用Task类的构造函数
        Task task1 = new Task(TaskMethod, "使用Task类的构造函数开始任务");
        task1.Start();
    }

    三、创建同步任务

    /// <summary>
    /// 同步任务
    /// </summary>
    static void SyncTask()
    {
        TaskMethod("主线程");
        Task task = new Task(TaskMethod, "同步任务");
        task.RunSynchronously();
    }

    四、创建长时间运行的任务

    /// <summary>
    /// 创建长时间运行的任务
    /// </summary>
    static void LongRunTask()
    {
        //创建一个新的线程,而不是使用线程池中的线程
        Task task = new Task(TaskMethod, "长时间运行任务", TaskCreationOptions.LongRunning);
        task.Start();
    }

    五、任务的结果

    /// <summary>
    /// 任务的结果
    /// </summary>
    static void TaskResult()
    {
        Task<string> task = new Task<string>((name) =>
        {
            Console.WriteLine(name);
            return string.Format("我叫:{0}", name);
        }, "张三");
        task.Start();
        Console.WriteLine(task.Result);
        task.Wait();
    }

    六、连续任务

    /// <summary>
    /// 连续任务
    /// </summary>
    static void ContinueTask()
    {
        Task task1 = new Task(() =>
        {
            Console.WriteLine("任务开始:{0}", Task.CurrentId);
            Thread.Sleep(3000);
        });
        //task1结束后会立即执行task2
        Task task2 = task1.ContinueWith((task) =>
        {
            Console.WriteLine("完成任务:", task.Id);
            Console.WriteLine("当前任务:", Task.CurrentId);
            Console.WriteLine("执行一些清理工作");
            Thread.Sleep(3000);
        });
        task1.Start();
    }

    七、任务层次结构

    /// <summary>
    /// 父子任务
    /// </summary>
    static void ParentAndChildTask()
    {
        Task parent = new Task(ParentTask);
        parent.Start();
        Thread.Sleep(2000);
        Console.WriteLine(parent.Status);
        Thread.Sleep(4000);
        Console.WriteLine(parent.Status);
    }
    static void ParentTask()
    {
        Console.WriteLine("任务编号:", Task.CurrentId);
        Task child = new Task(ChildTask);
        child.Start();
        Thread.Sleep(1000);
        Console.WriteLine("开始子任务");
    }
    static void ChildTask()
    {
        Console.WriteLine("子任务");
        Thread.Sleep(5000);
        Console.WriteLine("子任务完成");
    }

    八、取消任务

    //取消任务
    static void CancelTask()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        cts.Token.Register(() => { Console.WriteLine("任务取消后执行"); });
    
        Task task = new Task(() =>
        {
            Console.WriteLine("开始任务");
            for (int i = 0; i < 100; i++)
            {
                CancellationToken token = cts.Token;
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("任务已取消");
                    token.ThrowIfCancellationRequested();
                }
                Thread.Sleep(100);
                Console.WriteLine("循环编号:{0}", i);
                if (i == 5) cts.Cancel();
            }
        }, cts.Token);
        task.Start();
        try
        {
            task.Wait();
        }
        catch (AggregateException ex)
        {
            Console.WriteLine("异常:{0},{1}", ex.GetType().Name, ex.Message);
            foreach (var innerException in ex.InnerExceptions)
            {
                Console.WriteLine("内部异常:{0},{1}",
                    ex.InnerException.GetType().Name, ex.InnerException.Message);
            }
        }
    }

    ThreadPool线程池

    static void ThreadPoolDemo()
    {
        int workerThreads;
        int completionPortThreads;
        ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
        Console.WriteLine("工作线程:{0},IO线程:{1}", workerThreads, completionPortThreads);
        for (int i = 0; i < 5; i++)
        {
            ThreadPool.QueueUserWorkItem(TaskMethod);
        }
    }
    static void TaskMethod(object state)
    {
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("循环:{0},线程:{1}", i, Thread.CurrentThread.ManagedThreadId);
        }
    }

    Thread类

    一、创建一个线程

    /// <summary>
    /// 创建一个线程
    /// </summary>
    static void CreateThread()
    {
        Thread thread = new Thread(() =>
        {
            Console.WriteLine("这是一个线程,{0}", Thread.CurrentThread.ManagedThreadId);
        });
        thread.Start();
        Console.WriteLine("这是一个主线程,{0}", Thread.CurrentThread.ManagedThreadId);
    }

    二、给线程传递数据

    1、方式1,使用带ParameterizedThreadStart委托参数的Thread构造函数

    /// <summary>
    /// 给线程传递数据(方式1)
    /// </summary>
    static void ThreadWithData1()
    {
        var data = new { Message = "这是一条消息" };
        Thread thread = new Thread((state) =>
        {
            var obj = (dynamic)state;
            Console.WriteLine(obj.Message);
        });
        thread.Start(data);
    }

    2、方式2,创建一个自定义类,把线程的方法定义为实例方法,这样就可以初始化实例的数据之后,启动线程。

    class MyThread
    {
        private string message = string.Empty;
        public MyThread(string message)
        {
            this.message = message;
        }
    
        public void ThreadMain()
        {
            Console.WriteLine(message);
        }
    }
    /// <summary>
    /// 给线程传递数据(方式2)
    /// </summary>
    static void ThreadWithData2()
    {
        MyThread myThread = new MyThread("这是一条消息");
        Thread thread = new Thread(myThread.ThreadMain);
        thread.Start();
    }

    三、后台线程

    /// <summary>
    /// 后台线程
    /// </summary>
    static void BackgroundThread()
    {
        Thread thread = new Thread(() =>
        {
            Console.WriteLine("线程开始启动:{0}",Thread.CurrentThread.Name);
            Thread.Sleep(3000);
            Console.WriteLine("线程完成启动:{0}", Thread.CurrentThread.Name);
        });
        thread.Name = "MyThread";
        thread.IsBackground = true;
        thread.Start();
    }

    线程问题

    一、争用条件

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

    下面的例子通过lock关键字解决”争用条件”问题

    public class StateObject
    {
        private int state = 5;
        private object sync = new object();
    
        public void ChangeState(int loop)
        {
            lock (sync)
            {
                if (state == 5)
                {
                    state++;
                    Trace.Assert(state == 6, "循环" + loop + "次后满足条件");
                }
                state = 5;
            }
        }
    }
    public class SampleTask
    {
        public void RaceCondition(object o)
        {
            Trace.Assert(o is StateObject, "参数类型必须是StateObject");
            StateObject state = o as StateObject;
    
            int i = 0;
            while (true)
            {
                lock (state)
                {
                    state.ChangeState(i++);
                }
            }
        }
    }
    static void RaceConditions()
    {
        var state = new StateObject();
        for (int i = 0; i < 2; i++)
        {
            Task.Factory.StartNew(() => 
            {
                new SampleTask().RaceCondition(state);
            });
        }
    }

    二、死锁

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

    private StateObject s1;
    private StateObject 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("运行{0}", i);
                }
            }
        }
    }
    public void Deadlock2()
    {
        int i = 0;
        while (true)
        {
            lock (s2)
            {
                lock (s1)
                {
                    s1.ChangeState(i);
                    s2.ChangeState(i++);
                    Console.WriteLine("运行{0}", i);
                }
            }
        }
    }
    static void DeadLock()
    {
        var state1 = new StateObject();
        var state2 = new StateObject();
        Task.Factory.StartNew(new SampleTask(state1, state2).Deadlock1);
        Task.Factory.StartNew(new SampleTask(state1, state2).Deadlock2);
    }

    三、同步

    1、SharedState类说明了如何使用线程之间的共享状态,并共享一个整数值。

    2、将SharedState变量的State递增50000次

    #region 方式一
    public class ShareState
    {
        public int State { get; set; }
    }
    public class Job
    {
        ShareState shareState;
        public Job(ShareState shareState)
        {
            this.shareState = shareState;
        }
        public void DoTheJob()
        {
            for (int i = 0; i < 50000; i++)
            {
                lock (shareState)
                {
                    shareState.State += 1;
                }
            }
        }
    }
    #endregion
    #region 方式二
    public class ShareState
    {
        private int state = 0;
        private object syncRoot = new object();
        public int State
        {
            get { return state; }
        }
        public int IncrementState()
        {
            lock (syncRoot)
            {
                return ++state;
            }
        }
    }
    public class Job
    {
        ShareState shareState;
        public Job(ShareState shareState)
        {
            this.shareState = shareState;
        }
        public void DoTheJob()
        {
            for (int i = 0; i < 50000; i++)
            {
                lock (shareState)
                {
                    shareState.IncrementState();
                }
            }
        }
    }
    #endregion
    #region 方式三:使用Interlocked类用于使变量的简单语句原子化
    public class ShareState
    {
        private int state = 0;
        /// <summary>
        /// i++不是线程安全的,它的操作包括从内存中获取一个值,给该值递增1,再将它存储会内存。
        /// 这些操作都可能会被线程调度器打断。
        /// Interlocked类提供了以线程安全的方式递增、递减、交换和读取值的方式
        /// </summary>
        public int State
        {
            get { return state; }
        }
        public int IncrementState()
        {
            return Interlocked.Increment(ref state);
        }
    }
    public class Job
    {
        ShareState shareState;
        public Job(ShareState shareState)
        {
            this.shareState = shareState;
        }
        public void DoTheJob()
        {
            for (int i = 0; i < 50000; i++)
            {
                shareState.IncrementState();
            }
        }
    }
    #endregion

    3、在Main方法中,创建一个SharedState对象,并把它传递给20个Task对象的构造函数。

    class Program
    {
        static void Main(string[] args)
        {
            int numTasks = 20;
            var state = new ShareState();
            var tasks = new Task[numTasks];
            for (int i = 0; i < numTasks; i++)
            {
                tasks[i] = Task.Factory.StartNew(new Job(state).DoTheJob);
            }
            for (int i = 0; i < numTasks; i++)
            {
                tasks[i].Wait();
            }
            Console.WriteLine("累计{0}", state.State);
            Console.ReadKey();
        }
    }
    作者:灬花儿灬
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    [转载]要死的人都后悔些什么
    mysql优化之explain备忘笔记
    memcached 命令操作详解
    linux netstat 命令详解
    linux awk 命令详解
    定时任务FluentScheduler 学习笔记 .net
    webAPI文件上传时文件过大404错误的问题
    用递归形成树结构数据
    webAPP 图片上传
    webAPP踩坑记录
  • 原文地址:https://www.cnblogs.com/flower1990/p/7133724.html
Copyright © 2020-2023  润新知