首页 > 其他分享 >多线程

多线程

时间:2024-04-07 19:45:25浏览次数:13  
标签:Task Console Thread static WriteLine 线程 多线程

参考转载自:.NET多线程 - Broder - 博客园 (cnblogs.com)

一、进程和线程定义

进程:一个程序在服务器上运行时,占用的计算机资源合集,就是进程。

线程:是程序能够独立运行的最小单位。线程具有进程所具有的特征,所以线程又叫轻型进程

二、多线程

线程分为原生线程和托管线程,原生线程生命周期由操作系统管理,托管线程由.net程序管理

线程分为前台线程和后台线程,后台线程并非程序不可或缺的一部分。反之,如果所有前台进程都结束,程序也将结束,所有后台线程也将被杀死。

三、启动线程的方式

Thread的缺陷

  1. 官方提供了丰富的API,Thread操纵的是托管线程,然后对CPU发出指令,操控原生线程。这就导致响应不灵敏,无法很好的控制线程。
  2. Thread对启动线程数量不设控制,如果使用不当,会造成死机。       

ThreadPool缺陷

        1.提供的API太少了,无法操控线程。虽然可以使用ManualResetEvent来进行阻塞和恢复线程,但是操作还是不方便。

Task

1.类 Task 表示一个不返回值且通常异步执行的单个操作

2.派生类 Task 表示返回值且通常异步执行的单个操作

3.Task的工作通常以异步方式在线程池线程上执行

Task的构建

实例化对象


 
Task task = new Task(() =>
     {
          Console.WriteLine("");
     });
task.Start();//开启了一个新的线程
使用Run方法


 
Task.Run(() =>
{
   Console.WriteLine("");
});
使用Factory工厂


 
TaskFactory taskFactory = Task.Factory;
taskFactory.StartNew(() =>
{
     Console.WriteLine("");
});
使用Delay创建在指定的毫秒数后完成的任务


 
Task task = Task.Delay(2000).ContinueWith(t =>  //任务在2000ms 之后执行
{
     Console.WriteLine("");
});

 

线程异常

使用AggregateException类型异常

 
try {
    Task.WaitAll(tasks);
}
catch (AggregateException ae) {
    Console.WriteLine("One or more exceptions occurred:");
    foreach (var ex in ae.InnerExceptions)
        Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message);
}

 

线程取消

通常情况下,多线程中一个线程出现异常,其它的线程任务就不用进行了,我们如何取消线程

代码演示

 
  /// <summary>
        /// 取消线程
        /// </summary>
        public static void CancelThreadDemo()
        {
            CancellationTokenSource cts = new CancellationTokenSource();// 通知式的
            try
            {
                List<Task> taskList = new List<Task>();
                for (int i = 0; i < 100; i++)
                {
                    string name = $"btnThreadCore_Click_{i}";
                    int k = i;
                    taskList.Add(Task.Run(() =>
                    {
                        if (k == 5)
                        {
                            throw new Exception($"{name} 异常了");
                        }
                        if (!cts.IsCancellationRequested)//是否取消
                        {
                            Console.WriteLine($"this is {name} Ok!");
                        }
                        else
                        {
                            Console.WriteLine($"this is {name} Stop!");
                        }
                    }));
                };
                Task.WaitAll(taskList.ToArray());
            }
            catch (AggregateException aex)  //能够有多个Catch 在匹配异常类型的时候,先具体,而后在寻找父类
            {
                cts.Cancel(); //执行该方法之后,IsCancellationRequested会被指定为false
                foreach (var exception in aex.InnerExceptions)
                {
                    Console.WriteLine(exception.Message);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }

 

Task的属性

CompletedTask获取一个已成功完成的任务。
CurrentId 返回当前正在执行 Task 的 ID。
Exception 获取导致 AggregateException 提前结束的 Task。 如果 Task 成功完成或尚未引发任何异常,这将返回 null
Factory 提供对用于创建和配置 Task 和 Task 实例的工厂方法的访问。
IsCompleted 获取一个值,它表示是否已完成任务。

 

 

 

 

 

Task的方法

Start()启动 Task,并将它安排到当前的 TaskScheduler 中执行。
Run(Action) 将在线程池上运行的指定工作排队,并返回代表该工作的 Task 对象。
Delay(Int32) 创建一个在指定的毫秒数后完成的任务。
ContinueWith(Action) 创建一个在目标 Task 完成时异步执行的延续任务。
Dispose() 释放 Task 类的当前实例所使用的所有资源。
Wait() 等待 Task 完成执行过程。
WhenAny(Task[]) 非阻塞,任何提供的任务已完成时,创建将完成的任务。
WhenAll(Task[]) 非阻塞,创建一个任务,该任务将在数组中的所有 Task 对象都已完成时完成。
WaitAny(Task[]) 阻塞线程,等待提供的任一 Task 对象完成执行过程。
WaitAll(Task[]) 阻塞线程,等待提供的所有 Task 对象完成执行过程。
                           

四、线程同步(线程锁)

定义:使并发执行的多个线程能够按照一定的规则(时序)共享系统资源。即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作

1、Interlocked

    /// <summary>
    /// 线程锁
    /// </summary>
    public class Sample05
    {
        //0 未使用, 1 正在使用.
        private static int usingResource = 0;
        private const int numThreads = 10;
        private const int numThreadIterations = 5;
        

        public static void InterLockedDemo()
        {
            //var result1 = Interlocked.Exchange(ref usingResource, 5);
            //var result2 = Interlocked.CompareExchange(ref usingResource, 10, 5);

            Thread myThread;
            Random rnd = new Random();
            //开启了10个线程
            for (int i = 0; i < numThreads; i++)
            {
                myThread = new Thread(new ThreadStart(MyThreadProc));
                myThread.Name = String.Format("Thread{0}", i + 1);
                Thread.Sleep(rnd.Next(0, 1000));
                myThread.Start();
            }
        }

        private static void MyThreadProc()
        {
            for (int i = 0; i < numThreadIterations; i++)
            {
                UseResource();
                Thread.Sleep(1000);
            }
        }

        static bool UseResource()
        {
            //判断资源是否被占用     
            if (0 == Interlocked.Exchange(ref usingResource, 1))
            {
                Console.WriteLine("{0} 获取锁", Thread.CurrentThread.Name);

                Thread.Sleep(500);

                Console.WriteLine("{0} 释放锁", Thread.CurrentThread.Name);

                //释放锁
                Interlocked.Exchange(ref usingResource, 0);
                return true;
            }
            else
            {
                Console.WriteLine("   {0} 获取锁失败", Thread.CurrentThread.Name);
                return false;
            }
        }
    }

 

2、自旋锁

自旋锁:自旋锁(Spinlock)是最简单的线程锁,基于原子操作实现

.NET提供对象

SpinWait

SpinLock

    /// <summary>
    /// 自旋锁
    /// </summary>
    internal class Sample06
    {
        //0 未使用, 1 正在使用.
        private static int _lock = 0;

        public static void SpinWaitDemo()
        {
            //var spinWait = new SpinWait();
            while (Interlocked.Exchange(ref _lock, 1) != 0)
            {
                Thread.SpinWait(1);
                //spinWait.SpinOnce();//(推荐)
                // 一定次数以内,核心大于1,Thread.SpinWait
                // 超过一定次数,核心等于1,交替使用Thread.Sleep(0)和Thread.Yield方法
                // 再超过一定次数,Thread.Sleep(1)

                // Sleep(0)实际上调用SleepEx系统函数
                // Yield()调用SwitchToThread的系统函数
            }
            {
                /*锁保护区
                  方法体*/
            }
            Interlocked.Exchange(ref _lock, 0);
        }

        /// <summary>
        /// SpinLock用法
        /// </summary>
        private static SpinLock _spinLock = new SpinLock();
        public static void SpinLockDemo()
        {
            bool lockTaken = false;
            try
            {
                _spinLock.Enter(ref lockTaken);
                {
                    /*锁保护区
                    方法体*/
                }
            }
            finally
            {
                if (lockTaken)
                {
                    _spinLock.Exit();
                }
            }
        }

    }

优点

  • 避免了上下文切换,效率高

缺点:

  • 方法体不适用于长时间运行的操作,不然会影响其它线程运行
  • 当前实现没有考虑到公平性,如果多个线程同时获取锁失败,按时间顺序第一个获取锁的线程不一定会在释放锁后第一个获取成功

3 互斥锁

基于原子操作线程调度方式来实现。

互斥锁->是否被获取->获取失败,不进行重试->进入等待队列

锁释放->查看等待队列中是否存在线程->唤醒等待线程->调度运行(比自旋慢很多)

.NET提供对象

Mutex

internal class Sample07
    {
        private static readonly Mutex _lock = new Mutex();
        /// <summary>
        /// 互斥锁简单使用
        /// </summary>
        public static void MutexDemo()
        {
            _lock.WaitOne();
            try
            {
                {
                    /*锁保护区*/
                    /*方法体*/
                }
            }
            catch (AggregateException ae)
            {
                throw ae;
            }
            finally
            {
                _lock.ReleaseMutex();//释放锁
                //如果锁不在使用,立即释放资源
                _lock.Dispose();
            }
        }
    }

优点

  • 支持跨进程,防止程序多开、共享系统资源
  • 支持冲入,实现递归锁

缺点

  • 效率低

 

4 混合锁(Lock)

混合锁的特征是在获取锁失败后像自旋锁一样重试一定的次数,超过一定次数之后(.NET Core 2.1 是30次)再安排当前进程进入等待状态

.NET提供对象

Monitor

Lock

锁的对象

通常我们都会锁私有的引用对象,锁的是对象的内存引用地址

严格地说,提供给 lock 的对象只是用来唯一地标识由多个线程共享的资源,所以它可以是任意类实例。

  1. public,会导致死锁或者锁竞争
  2. string, 由于string具有不变性和字符串驻留,意味着整个程序中给定字符串只有一个实例。
internal class Sample08
    {
        private static readonly object Lock = new object();
        public static void MonitorDemo() {

            var lockObj = Lock;
            var lockTaken = false;
            try
            {
                // 获取锁
                Monitor.Enter(lockObj, ref lockTaken);

                // 锁保护
                {
                    //方法体
                }
            }
            finally
            {
                // 释放锁
                if (lockTaken) Monitor.Exit(lockObj);
            }
        }
    }

5. 读写锁

ReaderWriterLock

定义支持单个写线程和多个读线程的锁。该锁的作用主要是解决并发读的性能问题,使用该锁,可以大大提高数据并发访问的性能,只有在写时,才会阻塞所有的读锁。

private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private static void WriteDemo()
{
    // 获取写入锁
    Lock.EnterWriteLock();
    try
    {
        {
            //IO操作
        }
    }
    catch (Exception ex)
    {             
    }
    finally
    {
        // 释放写入锁
        Lock.ExitWriteLock();
    }
}

 

标签:Task,Console,Thread,static,WriteLine,线程,多线程
From: https://www.cnblogs.com/daiwk/p/18119751

相关文章

  • 多线程环境中使用MyBatis时避免出现没有token的问题
    //很重要,在多线程没有token下面三行可以解决mybatis方法报错//1.通过当前的WebUtil.getRequest()获取Servlet请求的属性ServletRequestAttributesservletRequestAttributes=newServletRequestAttributes(WebUtil.getRequest());//2.将获取到的Servlet请求属性设置......
  • C++多线程:async、future、packaged_task、promise、shared_future的学习与使用(九)
    1、异步任务线程异步线程的概念:异步:就是非同步,同步就是必须一个一个的执行,异步可以两个事情一起干异步线程:异步线程就相当于把非关联的两件事分开找两个线程去执行,而分开的那个就是异步线程举例:例如登录信息,用户登录完毕主线程肯定是需要去及时响应用户的请求的,而系统设......
  • 【JAVA】JAVA多线程基础4
    目录一、synchronized关键字特性1、互斥2、刷新内存3、可重入二、synchronized使用方法1、直接修饰普通方法2、修饰静态方法3、修饰代码块三、volatile关键字一、synchronized关键字特性1、互斥synchronized会起到互斥效果,某个线程执行到某个对象的synchronized......
  • 多线程(33)ConcurrentHashMap
    ConcurrentHashMap是Java并发包中提供的一个线程安全的哈希表实现。与传统的同步容器相比,ConcurrentHashMap通过一种分段锁的机制实现了更高的并发度。本节将深入探讨其设计原理,结合源码进行分析,并通过代码示例来演示其使用方法。设计原理ConcurrentHashMap的设计理......
  • 多线程(34)CopyOnWriteArrayList
    CopyOnWriteArrayList是Java中一个线程安全的ArrayList变体,属于java.util.concurrent包。它通过在所有修改操作(如add,set等)上执行显式复制来实现线程安全。这种设计适用于列表读操作的数量远远大于写操作的场景。设计原理CopyOnWriteArrayList的基本思想是,每当......
  • C#-多线程
    线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。线程是**轻量级进程**。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了C......
  • 【Java EE】多线程(一)
    ......
  • easyExcel通用导出(非注解,多线程)
    1、基础类描述ExcelWriter(导出工具类)Query(通用查询)Consumer(函数参数)SpringBeanUtil(获取bean)2、代码ExcelWriterimportcn.hutool.core.collection.CollUtil;importcn.hutool.core.collection.ListUtil;importcn.hutool.core.util.PageUtil;importcn.hutool.core.u......
  • Java:多线程-继承Thread类
    在Java中,通过继承Thread类是实现多线程的一种方式。这种方式允许你创建自己的线程类,并定义线程执行的具体内容。以下是关于继承Thread类的详细讲解:继承Thread类的步骤创建线程类:创建一个继承自Thread的子类。重写run方法:在子类中重写run方法,定义线程执行的任务。run方法是......
  • Java:多线程相关知识概念
    Java中的多线程是指在单个程序中并行执行多个线程(即执行路径或任务)的能力。多线程在Java中是一个核心概念,它允许应用程序更有效地利用CPU资源,同时还能进行并发操作。以下是Java中多线程相关的详细知识:线程的基本概念线程(Thread):是操作系统能够进行运算调度的最小单位。它......