首页 > 代码库 > 总结:任务、线程和同步

总结:任务、线程和同步

近期又看了一遍《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 = http://www.mamicode.com/{ "张三", "李四", "王五", "赵六", "钱七" };
    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 = http://www.mamicode.com/{ "张三", "李四", "王五", "赵六", "钱七" };
    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 = http://www.mamicode.com/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();
}

总结:任务、线程和同步