总结:任务、线程和同步

近期又看了一遍《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();
    }
}
posted @ 2017-07-07 18:53  灬花儿灬  阅读(550)  评论(0编辑  收藏  举报