七、多线程进阶学习一之Thread、ThreadPool

1、Thread相关基本操作

private void ThreadBasicOperation()
{
    ThreadStart threadStart = () =>//ThreadStart是一个委托,表示在System.Threading.Thread上执行的方法
    {
        Thread.Sleep(3000);
        this.DoSomethingLong("btnThreadClick ThreadStart");
        Thread.Sleep(3000);
    };
    Thread thread = new Thread(threadStart);
    thread.Start();

    //一下四个暂停、停止、恢复,不是很靠谱,其实做不到马上暂停
    //thread.Suspend();//挂起、暂停
    //thread.Resume();//恢复暂停的线程
    //thread.Abort();//停止,对外抛出ThreadAbortException异常
    //thread.ResetAbort();//让停止的线程继续运行

    //1、线程等待
    //就可以通过ThreadState判断状态来做到线程等待
    //在主线程通过ThreadState一直判断子线程状态是否已执行完毕
    //while (thread.ThreadState != ThreadState.Stopped)
    //{
    //    Thread.Sleep(100);
    //}
    //2、Join等待
    //thread.Join();//主线程等待子线程计算完成
    //thread.Join(2000);//主线程等待子线程2000毫秒,过时不候
    //thread.Priority = ThreadPriority.Normal;//线程优先级,其实是提高优先执行的概率,有意外
    优先执行并不代表优先结束,千万不要用这个来控制线程的执行顺序
    //thread.IsBackground = false;//判断是否为后台线程,false表示前台线程
    若为前台线程,进程关闭,进程执行完计算才消失

    //带参数启动线程
    ParameterizedThreadStart parameterizedThreadStart = s =>
    {
        this.DoSomethingLong($"btnThreadClick ParameterizedThreadStart {s}");
    };
    Thread threadPar = new Thread(parameterizedThreadStart);
    threadPar.Start("牧舟");
}

多线程基本操作

2、控制线程执行顺序

private void ControlThreadOrder01()
{
    //想要控制线程执行顺序:
    //1、回调,其实是第一个线程里的动作执行完毕以后去执行第二个动作
    [什么是回调?](https://blog.csdn.net/zjpp2580369/article/details/83027547)
    //2、既然是子线程来执行,必然是异步的,不能卡界面
    //扩展封装一个:
    ThreadStart threadStart = () =>
    {
        Thread.Sleep(2000);
        this.DoSomethingLong("扩展封装,回调委托");
        Thread.Sleep(2000);
    };
    Action actionCallBack = () =>
    {
        Console.WriteLine("好好学习,天天向上!");
    };
    this.ThreadWithCallBack(threadStart, actionCallBack);
}
/// <summary>
/// 控制线程顺序
/// 两个委托封装回调
/// </summary>
/// <param name="threadStart"></param>
/// <param name="actionCallBack"></param>
private void ThreadWithCallBack(ThreadStart threadStart,Action actionCallBack)
{
    //之前可以用来控制线程执行顺序的方法:
    //1、while死循环  判断子线程状态  等待
    //2、Join
    //以上两种方法因为都需要主线程去等待子线程执行完,因此都会卡界面

    ThreadStart method = () =>
    {
        actionCallBack.Invoke();
        threadStart.Invoke();
    };
    //把两个逻辑用同一个委托包装,然后开启子线程去回调委托
    //这样,两个逻辑在子线程中就会按顺序执行了
    Thread thread = new Thread(method);
    thread.Start();
}

扩展封装控制线程执行顺序

3、获取异步结果,不卡顿

private void ControlThreadOrder01WithReturn()
{
    //1.需要获取异步线程的结果
    //2.不能卡顿界面
    Func<int> func = () =>
    {
        Thread.Sleep(3000);
        return DateTime.Now.Year;
    };
    //int year = this.ThreadWithReturnT<int>(func);
    //Console.WriteLine($"今年是 {year} 年……");

    Func<int> funcCallBack = this.ThreadWithReturnFunc<int>(func);//这里不卡界面
    //主线程可以在这里继续执行自己的任务
    int year = funcCallBack.Invoke();//这里会卡界面
    Console.WriteLine($"今年是 {year} 年……");
}

/// <summary>
/// 这种方式会等待异步执行结果,造成界面卡顿
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
private T ThreadWithReturnT<T>(Func<T> func)
{
    //1、既需要异步执行
    //2、又需要得到计算结果
    //也就是说既不想等待(那就需要异步执行),又想要结果,因为得到结果需要时间
    //根本不可能做到

    //回调?回调解决的问题是,多个耗时逻辑需要有序执行但又不能卡顿界面,即不能等待
    //所以通过回调,把耗时逻辑放在子线程中去执行
    //可是我们现在需要拿到异步逻辑中的结果
    T t = default(T);
    ThreadStart threadStart = () =>
    {
        t = func.Invoke();
    };
    Thread thread = new Thread(threadStart);
    thread.Start();
    thread.Join();
    //这里不Join的话,主线程不会等待,直接就返回t了
    //但是,这里拿到结果需要3秒,因此,不会拿到结果的
    //如果这里thread.Join()的话,肯定是能拿到异步执行结果,但是会卡顿界面
    return t;
}
/// <summary>
/// 花式玩法:返回一个委托
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
private Func<T> ThreadWithReturnFunc<T>(Func<T> func)
{
    T t = default(T);
    ThreadStart threadStart = () =>
    {
        t = func.Invoke();
    };
    Thread thread = new Thread(threadStart);
    thread.Start();//开启了一个新的线程,不卡界面
    //其实开启的这个线程已经开始异步执行需要的委托func了
    //但是这个时候主线程还可以继续执行自己的任务

    return new Func<T>(() =>
    {
        thread.Join();//阻塞主线程,等待thread线程执行完毕
        //这里之所以Join是为了保证一定能够拿到异步执行的结果
        //其实一般来说,当主线程需要获取异步结果的时候,子线程已经有结果了
        return t;
    });
}

4、线程池分配线程

/// <summary>
/// 线程池如何分配一个线程
/// </summary>
private void AllocationThread()
{
    //WaitCallback表示线程池要执行的回调方法
    WaitCallback waitCallback = o =>
    {
        Console.WriteLine($"参数:{o}");
        this.DoSomethingLong("AllocationThread");
    };
    //将方法排队以执行。该方法在线程池线程可用时执行。
    ThreadPool.QueueUserWorkItem(waitCallback);
    ThreadPool.QueueUserWorkItem(waitCallback, "好好学习,天天向上!");
}

线程池分配一个线程

5、线程池获取和设置线程数

/// <summary>
/// 线程池获取和设置线程数
/// </summary>
private void SetTheNumberOfThreads()
{
    Console.WriteLine("================获取线程池中最大、最小线程数================");
    //workerThreads:工作线程主要是用作管理CLR内部对象的运行,通常用于计算密集的任务
    //completionPortThreads:I/O(Input/Output)线程,主要用于与外部系统交互信息,如输入、输出
    ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads);
    Console.WriteLine($"Min this workerThreads={minworkerThreads}, this completionPortThreads={mincompletionPortThreads}");
    ThreadPool.GetMaxThreads(out int maxworkerThreads, out int maxcompletionPortThreads);
    Console.WriteLine($"Min this workerThreads={minworkerThreads}, this completionPortThreads={mincompletionPortThreads}");
    Console.WriteLine("================设置线程池中线程的数量================");
    ThreadPool.SetMinThreads(8, 8);
    ThreadPool.SetMaxThreads(64, 64);//设置的最大值不能小于计算机的逻辑线程数
    ThreadPool.GetMinThreads(out int defminworkerThreads, out int defmincompletionPortThreads);
    Console.WriteLine($"Min this defminworkerThreads={defminworkerThreads}  this defmincompletionPortThreads={defmincompletionPortThreads}");
    ThreadPool.GetMaxThreads(out int defmaxworkerThreads, out int defmaxcompletionPortThreads);
    Console.WriteLine($"Max this defmaxworkerThreads={defmaxworkerThreads}  this defmaxcompletionPortThreads={defmaxcompletionPortThreads}");

    //线程池中的线程可以设置,但是不要随便折腾,因为设置以后,线程相对于当前进程而言,是全局的
    //Task、Parallel等都是来自于线程池
    //new Thread又可以新开一个线程,会占一个线程池的位置
}

6、观望式线程等待

/// <summary>
/// 线程等待:观望式
/// </summary>
private void WaitAndSeeThreadWaiting()
{
    //MaualResetEvent默认要求参数状态false,表示这个观望开关关闭,可通过mre.Set()打开
    //MaualResetEvent参数状态为true,表示这个观望开关打开,可通过mre.Reset()关闭
    ManualResetEvent mre = new ManualResetEvent(false);
    //mre默认设置为false,就是一个开关,观望着监视着什么时候设置为true
    //mre设置为false的话,就是主线程遇到mre.WaitOne时,要等待mre设置为true
    //才能按顺序执行下面的代码
    ThreadPool.QueueUserWorkItem(o =>
    {
        Console.WriteLine($"参数o:{o}");
        this.DoSomethingLong("牧舟=牧舟=牧舟");
        mre.Set();//将ManualResetEvent的状态false变为true
    });
    Console.WriteLine("mre.WaitOne之前的代码……");
    mre.WaitOne();//只要是mre状态变为true,就继续往后执行
    //主线程执行到WaitOne时等待,直到mre.Set()把ManualResetEvent的状态变为true后继续往下执行
    Console.WriteLine("mre.WaitOne之后的代码……");

    Console.WriteLine("任务全部完成了");
}

观望式线程等待

7、线程死锁

/// <summary>
/// 线程死锁
/// </summary>
private void ThreadDeadLock()
{
    ThreadPool.SetMaxThreads(4, 4);//设置后,线程池里最多只能分配4个线程
    //死锁
    ManualResetEvent mre = new ManualResetEvent(false);
    for(int i = 0; i < 10; i++)
    {
        int k = i;
        ThreadPool.QueueUserWorkItem(t =>
        {
            Console.WriteLine($"ThreadId = {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            if (k == 9)
            {
                mre.Set();
            }
            else
            {
                mre.WaitOne();
            }
        });
    }
    if (mre.WaitOne())//只有mre.Set()执行以后,状态值为true,才能往后执行
                      //主线程等待前面分配的所有子线程,都必须完成计算以后才往后执行
    {
        Console.WriteLine("所有任务执行完成……");
    }
}

线程死锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值