C# 多线程编程一

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
实时数仓Hologres,5000CU*H 100GB 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
简介: C# 多线程编程

多线程编程01

线程概念

线程可以被描述为它所处的进程中的一个微进程,它拥有起点,执行的顺序系列和一个终点。

线程是在进程的内部执行的指令序列,由进程负责管理和调度

在进程内的每个线程共享相同的内存空间和数据资源

进程中的每个线程共享代码区,即不同的线程可以执行同样的函数

即线程组成进程

线程和进程的联系:

进程拥有自己独立的内存空间和数据,进程内的所有线程是共享内存空间和数据的。

进程对应着一段程序,它是由一些在同一个程序里面独立的同时的运行的线程组成的。

线程的运行依赖与进程提供的上下文环境,并且使用的是进程的资源。

进程是系统进行资源分配的基本单位。

线程是操作系统CPU时间的基本单元,是系统中最小的执行单元。

多线程

通常指的是多线程编程

是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

好处:

可以提高CPU的利用率

在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率

缺点:

多线程本身可能影响系统性能的不利方面

线程也是程序,所以线程需要占用内存,线程越多占用内存也越多

多线程需要协调和管理,所以需要CPU时间跟踪线程

线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题

线程太多会导致控制太复杂,容易出现造成很多Bug又不容易排除的情况

线程的调度

1) 每一个线程的优先级是0到31。高优先级的线程ready之后,不管低优先级的线程在做什么,立即上位,没话说。Windows会把最高优先级的不同线程调度到各个CPU上并行执行,多核多处理器谁也不闲着。

2) Windows制定进程有6个优先等级,线程有7个,通过组合来得出实际的线程优先级0到30(0优先级保留给Windows用于内存释放)。CLR保留了线程优先级中的最低和最高级,供程序员可设置的只有5个等级。

3) 进程的优先级是一个虚拟的概念,只是为了帮助用于映射到1-31中的某个等级,一般来说进程的等级默认为创建它的进程的等级。很多进程都是Windows Explorer创建的,默认也就是Nomral这个等级,说白了我们的线程在大多情况下映射到Windows线程优先级为6-10。

为什么说线程是比较昂贵的?

•1)从内存上来说,(对于32位架构)每一个线程包含线程内核对象(700字节)/线程环境块(4KB)/内核堆栈(12KB)/用户堆栈(1MB)。并且可以发现,这1MB的用户堆栈内存在CLR线程创建的时候完全分配,并不是动态增加的(Windows线程的创建只是保留1MB的内存空间)。

•2)从线程切换上来说,需要做哪些步骤来进行切换?首先是把CPU寄存器中的值保存到当前线程的内核对象中,然后如果线程切换到不同CPU的话需要为CPU准备新的虚拟地址空间,最后把目标线程内核对象中寄存器的值复制到CPU寄存器中。

•3) 更大的性能损害来自于,线程切换之后缓存中的数据可能会不能命中,需要重新准备这些数据。

•4) 此外,在垃圾回收的时候,CLR会挂起所有线程,查看线程堆栈,垃圾回收压缩后重置堆栈指针地址。

多线程和多进程面临的问题

并发

指多个进程同时执行(单CPU里其实是轮询执行),但其实多个进程共享的是同一个CPU、内存及IO设备,由此产生了资源竞争的情况。为解决这样的情况,才出现了同步和互斥。也可以说是并发问题引出了同步和互斥技术。——同时执行

同步

多个进程间相互依赖,也就是说B进程要执行的条件是A进程执行完后输出相应结果,B进程得到A进程的运行结果后才能顺利执行,这种A等待B的情况叫同步。——有依赖关系

死锁

当A进程在等B进程释放资源(或锁),B进程又同时需要等待某个资源的释放,这个资源又刚好被A进程占有,这样就引发了死锁。——两个进程互相等待

饥饿

当A进程等待B进程释放资源(或锁),B进程在释放资源(或锁)前死掉了,这样A进程就处于一直等待的情况,这样引发了A进程的饥饿问题。

同步异步demo:

using System;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;

namespace day30test03
{
   
    class Program
    {
   
        /// <summary>
        /// 同步异步调用
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
   
            Console.WriteLine($"主线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}{System.Threading.Thread.CurrentThread.Priority}{System.Threading.Thread.CurrentThread.ThreadState}{System.DateTime.Now}");
            Console.WriteLine("同步");
            //MessageBox.Show("1");
            //Console.ReadLine();
            //全部都由主线程执行,所以每一遍打印都是上一次完成之后才能进行
            Work work = new Work();
            for(int i = 0; i < 10; i++)
            {
   
                work.show();
            }
            Console.WriteLine("ok");
            Console.WriteLine("异步");
            //每次都创建一个新的子线程来执行打印
            for(int j = 0; j < 10; j++)
            {
   
                Work work1 = new Work();
                work1.Sayshow();
            }
        }
    }
    class Work
    {
   
        //定义私有字段的线程
        private System.Threading.Thread t;
        //C#内定义好的一个无参数无返回值的委托
        private System.Threading.ThreadStart ts;
        public void show()
        {
   
            Console.WriteLine($"子线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}  {System.Threading.Thread.CurrentThread.Priority}  {System.Threading.Thread.CurrentThread.ThreadState} {System.DateTime.Now}");
            System.Threading.Thread.Sleep(10);
        }
        public void Sayshow()
        {
   
            //委托,将方法作为参数传入
            this.ts = new System.Threading.ThreadStart(this.dowork);
            //线程的构造方法,需要传递一个委托,线程启动时会调用委托执行方法
            this.t = new System.Threading.Thread(ts);
            this.t.Priority = System.Threading.ThreadPriority.Highest;
            this.t.Name = "Sayshow thread";
            this.t.IsBackground = true;
            this.t.Start();
        }
        public void dowork()
        {
   
            Console.WriteLine($"子线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}  {System.Threading.Thread.CurrentThread.Priority}  {System.Threading.Thread.CurrentThread.ThreadState} {System.DateTime.Now} ");
            System.Threading.Thread.Sleep(10);
        }
    }
}

多线程的传参问题:

1.静态字段传递

安全性不够,缺点很多

using System;
using System.Runtime.InteropServices.ComTypes;

namespace day30test05
{
   
    class Program
    {
   
        [ThreadStatic]  //声明这是线程调用的静态字段 尽可能的避免数据争用
        public static String str = "111静态传参";
        /// <summary>
        /// 多线程的传参问题
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
   
            Console.WriteLine($"主线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}{System.Threading.Thread.CurrentThread.Priority}{System.Threading.Thread.CurrentThread.ThreadState}{System.DateTime.Now}");
            //Console.WriteLine("同步");
            //MessageBox.Show("1");
            //Console.ReadLine();
            /*Work work = new Work();
            for (int i = 0; i < 10; i++)
            {
                work.show();
            }
            Console.WriteLine("ok");
            Console.WriteLine("异步");
            for (int j = 0; j < 10; j++)
            {
                Work work1 = new Work();
                work1.Sayshow();
            }*/
        }
    }
    class Work
    {
   
        private System.Threading.Thread t;
        private System.Threading.ThreadStart ts;
        public void show()
        {
   
            Console.WriteLine($"子线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}  {System.Threading.Thread.CurrentThread.Priority}  {System.Threading.Thread.CurrentThread.ThreadState} {System.DateTime.Now}");
            System.Threading.Thread.Sleep(10);
        }
        public void Sayshow()
        {
   
            this.ts = new System.Threading.ThreadStart(this.dowork);
            this.t = new System.Threading.Thread(ts);
            this.t.Priority = System.Threading.ThreadPriority.Highest;
            this.t.Name = "Sayshow thread";
            this.t.IsBackground = true;
            this.t.Start();
        }
        public void dowork()
        {
   
            Console.WriteLine($"子线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}  {System.Threading.Thread.CurrentThread.Priority}  {System.Threading.Thread.CurrentThread.ThreadState} {System.DateTime.Now} ");
            System.Threading.Thread.Sleep(10);
        }
    }
}

2.委托带参数传递

较安全,灵活性不够

using System;

namespace day30test06
{
   
    class Program
    {
   
        /// <summary>
        /// 多线程委托传递参数
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
   
            Test test = new Test();
            test.Sayshow("1多线程委托传递参数");
            Console.WriteLine("Hello World!");
        }
    }
    class Test
    {
   
        private System.Threading.Thread t;
        //private System.Threading.ThreadStart ts;
        private System.Threading.ParameterizedThreadStart tps;
        public void Sayshow(String str)
        {
   
            //this.ts = new System.Threading.ThreadStart(this.dowork);
            this.tps = new System.Threading.ParameterizedThreadStart(this.dowork);
            this.t = new System.Threading.Thread(tps);
            this.t.Priority = System.Threading.ThreadPriority.Highest;
            this.t.Name = "Sayshow thread";
            this.t.IsBackground = true;
            this.t.Start(str);
        }
        public void dowork(Object obj)
        {
   
            Console.WriteLine(obj);
            Console.WriteLine($"子线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}  {System.Threading.Thread.CurrentThread.Priority}  {System.Threading.Thread.CurrentThread.ThreadState} {System.DateTime.Now} ");
            System.Threading.Thread.Sleep(10);
        }
    }
}

3.对象封装传参

灵活性好,安全性较好,

using System;

namespace day30test08
{
   
    class Program
    {
   
        /// <summary>
        /// 对象封装传递参数
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
   
            for (int i = 0; i < 10; i++)
            {
   
                Test test = new Test();
                test.Sayshow(new FUZhu("对象封装传递参数"));
            }
            Console.WriteLine("Hello World!");
            Console.ReadLine();
        }
    }
    class Test
    {
   
        private System.Threading.Thread t;
        private System.Threading.ThreadStart ts;
        //private System.Threading.ParameterizedThreadStart tps;
        private object obj;

        public void Sayshow(object oj)
        {
   
            this.ts = new System.Threading.ThreadStart(this.dowork);
            this.t = new System.Threading.Thread(this.ts);
            this.t.Priority = System.Threading.ThreadPriority.Highest;
            this.t.Name = "Sayshow thread";
            this.t.IsBackground = true;
            this.obj = oj;
            this.t.Start();
        }
        public void dowork()
        {
   
            //Console.WriteLine(obj);
            //Console.WriteLine(1);
            Console.WriteLine($"子线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}  {System.Threading.Thread.CurrentThread.Priority}  {System.Threading.Thread.CurrentThread.ThreadState} {System.DateTime.Now} {(this.obj as FUZhu).Msg}");
            //System.Threading.Thread.Sleep(1000);
            //Console.WriteLine(2);
        }
    }
    class FUZhu
    {
   
        private String msg;
        public FUZhu(String str)
        {
   
            this.msg = str;
        }

        public string Msg {
    get => msg; set => msg = value; }
    }
}

疑问:

同样的代码,再.net core和.net framework中的实现却不一样,core实现不了framework的效果,可能是core对多线程的调度不一样。

委托异步以多线程的区别:

委托启动之后线程不能停止,无法被干涉,而多线程启动之后线程可被很好的控制。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace day30test09
{
   
    class Program
    {
   
        /// <summary>
        /// 多线程和委托的区别
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
   
            for (int i = 0; i < 10; i++)
            {
   
                Test test = new Test(); //委托使用多线程
                System.Threading.ThreadStart ts = new System.Threading.ThreadStart(test.dowork);
                ts.BeginInvoke(null, null);
            }
            Console.Read();
        }
    }
    class Test
    {
   
        private System.Threading.Thread t;
        private System.Threading.ThreadStart ts;
        //private System.Threading.ParameterizedThreadStart tps;
        private object obj;

        public void Sayshow(object oj)
        {
   
            this.ts = new System.Threading.ThreadStart(this.dowork);
            this.t = new System.Threading.Thread(this.ts);
            this.t.Priority = System.Threading.ThreadPriority.Highest;
            this.t.Name = "Sayshow thread";
            this.t.IsBackground = true;
            this.obj = oj;
            this.t.Start();
        }
        public void dowork()
        {
   
            //Console.WriteLine(obj);
            //Console.WriteLine(1);
            Console.WriteLine($"子线程:{System.Threading.Thread.CurrentThread.ManagedThreadId}  {System.Threading.Thread.CurrentThread.Priority}  {System.Threading.Thread.CurrentThread.ThreadState} {System.DateTime.Now}");
            //System.Threading.Thread.Sleep(1000);
            //Console.WriteLine(2);
        }
    }
}

Thread类的属性和方法

属 性 说 明
CurrentThread 静态属性,获取当前正在运行的线程
IsAlive 获取一个值,该值指示当前线程的执行状态
IsBackground 获取或设置一个值,该值指示是否是后台线程
Name 获取或设置线程的名称
Priority 获取或设置一个值,该值指示线程的调度优先级
ThreadState 获取一个值,该值包含当前线程的状态
方 法 说 明
Start 开始执行线程
Abort 终止线程
Join 阻止调用线程,直到被调用线程终止为止,它在被调用线程实际停止执行之前或可选超时间隔结束之前不会返回
Interrupt 打断处于WaitSleepJoin线程状态的线程,使其继续执行
Sleep 静态方法,使当前线程停止指定的毫秒数

ThreadState枚举的成员

Running,线程已启动,正在运行中。

Stopped,线程已停止。

Unstarted,尚未对线程调用Thread.Start方法。

WaitSleepJoin,由于调用Sleep或Join,线程已暂停。

Aborted,线程状态包括 AbortRequested 并且该线程现在已死,但其状态尚未更改为 Stopped

AbortRequested,已对线程调用了Thread.Abort 方法后的挂起状态。

线程方法和状态之间的关系

前提条件 结果
线程**A** 线程**B** 线程**A**状态 线程**B**状态
A**线程被创建但未调用Start方法** Unstarted
对**A调用A.Start()** Running
调用 Thread.Sleep() WaitSleepJoin
A**进入running状态之后,对B调用 B.Join()** 由**Running**变为 WaitSleepJoin Running
对A调用A. Interrupt() WaitSleepJoin 变为Running
A.Abort Stopped Running

Join方法

Join()

调用线程等待被调用线程完成所有操作后,当前线程才能继续执行

Join(int time)

调用线程等待被调用线程完成所有操作后或者等待时间超过指定时间time以后,当前线程才能继续执行

CPU时间片

什么是CPU时间片

线程靠抢CPU时间片而执行,谁抢的多谁利用CPU的时间就多也就执行得快

优先级

决定了这个争抢能力

线程优先级高的在同一时间越能获得CPU时间片

优先级枚举

ØThreadPriority

成员

Normal,默认情况下创建线程的优先级 ,原则上相同优先级的线程会获得相同的CPU时间。

AboveNormal,处于Normal优先级之上但低于Highest优先级。

BelowNormal,处于Normal优先级之下但高于Lowest优先级。

Highest,最高的优先级。

Lowest,低于BelowNormal的最低优先级。

线程同步

使线程协调一致的工作,最简单的额锁定方式——lock

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace day30test10
{
   
    class Program
    {
   
        /// <summary>
        /// 线程同步
        /// 使线程协调一致的工作
        /// 最简单的额锁定方式——lock
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
   
            Test tes = new Test();
            for(int i = 0; i < 10; i++)
            {
   
                System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(tes.gets));
                t.Start();
                System.Threading.Thread t1 = new System.Threading.Thread(new System.Threading.ThreadStart(tes.gets1));
                t1.Start();
                System.Threading.Thread t2 = new System.Threading.Thread(new System.Threading.ThreadStart(tes.gets2));
                t2.Start();
            }
        }
    }
    class Test
    {
   
        System.Collections.Hashtable ht = new System.Collections.Hashtable();
        int count;
        int[] counts;
        public int Count {
    get => count; set => count = value; }
        public int[] Counts {
    get => counts; set => counts = value; }
        public Hashtable Ht {
    get => ht; set => ht = value; }
        public Hashtable Ht1 {
   
            get
            {
   
                lock(this)
                {
   
                    return ht1;
                }
            }
            set
            {
   
                lock (this)
                {
   
                    ht1 = value;
                }
            }
        }

        public Hashtable Ht2 {
   
            get
            {
   
                if(this.ht2.IsSynchronized == false)
                {
   
                    return ht2.SyncRoot as System.Collections.Hashtable;
                }
                else {
    return ht2; }
            }
            set
            {
   
                lock (this)
                {
   
                    ht2 = value;
                }
            }
        }
        //没有使用任何的线程同步的操作
        public void gets()
        {
   
            System.Collections.Hashtable Ht1 =  this.Ht;
        }
        System.Collections.Hashtable ht1 = new System.Collections.Hashtable();
        //使用lock的方式处理线程同步
        public void gets1()
        {
   
            System.Collections.Hashtable Ht1 = this.Ht;
        }
        System.Collections.Hashtable ht2 = new System.Collections.Hashtable();
        //使用所有集合都有的IsSynchronized和SyncRoot属性来处理,最佳方式
        public void gets2()
        {
   
            System.Collections.Hashtable Ht1 = this.Ht;
        }
    }
}

Monitor监视器

用于代码块的锁定(较安全)

类似使用Lock关键字(更轻量,方便)

目录
相关文章
|
2月前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
17天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
65 12
|
15天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
98 2
|
2月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
1月前
|
缓存 Java 调度
多线程编程核心:上下文切换深度解析
在现代计算机系统中,多线程编程已成为提高程序性能和响应速度的关键技术。然而,多线程编程中一个不可避免的概念就是上下文切换(Context Switching)。本文将深入探讨上下文切换的概念、原因、影响以及优化策略,帮助你在工作和学习中深入理解这一技术干货。
48 10
|
2月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
1月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
1月前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
51 3
|
1月前
|
算法 调度 开发者
多线程编程核心:上下文切换深度解析
在多线程编程中,上下文切换是一个至关重要的概念,它直接影响到程序的性能和响应速度。本文将深入探讨上下文切换的含义、原因、影响以及如何优化,帮助你在工作和学习中更好地理解和应用多线程技术。
43 4
|
12天前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程