C#多线程开发详解

C#多线程开发详解

  • 持续更新中。。。。。
  • 一、为什么要使用多线程开发
    • 1.提高性能
    • 2.响应性
    • 3.资源利用
    • 4.任务分解
    • 5.并行计算
    • 6.实时处理
  • 二、多线程开发缺点
    • 1.竞态条件
    • 2.死锁和饥饿
    • 3.调试复杂性
    • 4.上下文切换开销
    • 5.线程安全性
  • 三、多线程开发涉及的相关概念
    • 常用概念
      • (1)lock
      • (2)查看当前工作线程信息
      • (3)主线程、前台线程、后台线程
    • 1.Thread(线程)
      • (1)创建线程
      • (2) 线程同步
      • (3)线程异步
    • 2.ThreadPool(线程池)
    • 3.Task(任务)
      • (1)Task与Thead的关系
    • 4.Task Parallel Library (TPL)(任务并行库)
    • 5.Async/Await(异步/等待)
    • 6.Monitor(监视器)
    • 7.Semaphore(信号量)
    • 8.SemaphoreSlim
    • 9.AutoResetEvent(自动复位事件)
    • 10.ManualResetEvent(手动复位事件)
    • 11.CancellationToken(取消标记)
    • 12.volatile(易失性修饰符)
    • 13.Mutex(互斥锁)
    • 14.ReaderWriterLock(读写锁)
    • 15.ReaderWriterLockSlim(轻量级读写锁)
    • 16.SpinLock
    • 17.SpinWait
    • 18.Barrier(屏障)
  • 四、多线程的异常捕获问题

持续更新中。。。。。

一、为什么要使用多线程开发

1.提高性能

多线程允许程序同时执行多个任务,从而有效利用多核处理器,加快程序的执行速度。特别是在需要处理大量计算、I/O 操作或并行任务的应用中,多线程可以显著提高性能。

2.响应性

多线程使应用能够同时处理多个用户请求或事件,提高了应用的响应性。例如,多线程可以保持用户界面的响应,即使在执行长时间操作时也能让用户继续交互。

3.资源利用

多线程可以更有效地利用系统资源,如内存和网络连接。这对于高并发服务器、网络应用和数据处理任务特别有用。

4.任务分解

将复杂任务分解为多个小任务,每个任务在不同的线程中执行,可以简化问题并提高可维护性。

5.并行计算

多线程可以用于并行计算,例如在科学计算、数据分析和图像处理领域。这有助于加速大规模计算。

6.实时处理

在实时系统中,多线程可以保证任务在规定的时间内完成,从而满足对时间敏感性的需求。

二、多线程开发缺点

1.竞态条件

多线程可能会导致竞态条件,即多个线程竞争访问共享资源,可能导致数据不一致性和错误。

2.死锁和饥饿

不正确的线程同步可能导致死锁(多个线程无法继续执行)或饥饿(某些线程无法获取所需资源)问题。

线程1,2启动,分别占用锁lock1,lock2。之后线程1请求lock2,但是线程2已经占用lock2,线程1无法继续执行,进入等待。线程2请求lock1,但是线程1已经占用lock1,线程2无法继续执行,进入等待。这里陷入死锁,线程1,线程2,都在等待对方释放锁来给自己使用,程序一直无法运行,一直在等待中。

using System;
using System.Threading;class DeadlockExample
{static object lock1 = new object();static object lock2 = new object();static void Main(){Thread thread1 = new Thread(Method1);Thread thread2 = new Thread(Method2);thread1.Start();thread2.Start();thread1.Join();thread2.Join();Console.WriteLine("Main thread finished.");}static void Method1(){lock (lock1){Console.WriteLine("Method1 acquired lock1.");Thread.Sleep(1000);Console.WriteLine("Method1 trying to acquire lock2.");lock (lock2){Console.WriteLine("Method1 acquired lock2.");}}}static void Method2(){lock (lock2){Console.WriteLine("Method2 acquired lock2.");Thread.Sleep(1000);Console.WriteLine("Method2 trying to acquire lock1.");lock (lock1){Console.WriteLine("Method2 acquired lock1.");}}}
}

3.调试复杂性

多线程程序的调试和错误跟踪可能会更加复杂,因为线程间的交互和排错可能变得更难。

4.上下文切换开销

上下文切换(Context Switching)是多线程环境中的一种操作,指的是在一个 CPU 核心上切换正在执行的线程,从当前线程的执行上下文(包括寄存器状态、程序计数器等)切换到另一个线程的执行上下文, 线程的切换需要额外的开销,因此在某些情况下,过多的线程可能会导致性能下降。

  1. 当一个线程的时间片(时间片轮转调度算法)用完,操作系统需要挂起该线程并切换到另一个线程。
  2. 当一个线程主动放弃 CPU,例如通过调用 Thread.Sleep()、Thread.Yield() 或等待某个事件时
    3.当一个线程被高优先级的线程抢占

上下文切换的过程涉及以下步骤:

  1. 保存当前线程的上下文: 操作系统将当前线程的寄存器状态、程序计数器等信息保存到该线程的内存空间中,以便稍后能够恢复该线程的执行
    2.恢复目标线程的上下文: 操作系统从目标线程的内存空间中恢复寄存器状态、程序计数器等信息,准备让目标线程继续执行。
  2. 切换内核堆栈: 每个线程都有自己的内核堆栈,上下文切换时,操作系统会切换内核堆栈,以确保线程的隔离性。

上下文切换开销指的是从一个线程切换到另一个线程的过程中所涉及的时间和资源开销。这些开销主要包括以下几个方面:

  1. 寄存器保存和恢复: 当线程切换时,操作系统需要保存当前线程的寄存器状态,然后恢复目标线程的寄存器状态。这涉及到大量的数据拷贝和计算。
    2.内存访问: 上下文切换过程中需要频繁访问内存,包括将寄存器状态和其他上下文信息写入内存,以及从内存中读取目标线程的上下文信息。
    3.调度开销: 操作系统需要决定要切换到哪个线程,这涉及到调度算法的开销,包括选择合适的线程并进行必要的线程队列操作。
    4.TLB(Translation Lookaside Buffer)失效: 当线程切换时,虚拟内存的映射可能会发生变化,导致 TLB 缓存失效,从而增加了内存访问的开销。

上下文切换开销会影响系统的整体性能,特别是在高并发、频繁切换的情况下。因此,在设计多线程应用程序时,需要考虑如何减少上下文切换的发生,以提高程序的执行效率。一些方法包括:

  1. 使用线程池:线程池可以减少线程的创建和销毁,从而减少上下文切换的频率。
  2. 合理设置线程数量:避免创建过多线程,以减少不必要的上下文切换。
    3.使用异步编程模型:使用异步操作和任务可以减少线程的使用,从而减少上下文切换。

5.线程安全性

多线程编程需要谨慎处理线程安全性,以避免数据竞争和共享资源的冲突。

三、多线程开发涉及的相关概念

常用概念

(1)lock

在 C# 中,lock 关键字用于实现线程同步,以确保在多线程环境中对共享资源的访问是安全的。lock 关键字会创建一个互斥锁(也称为监视器锁),只有一个线程可以获得该锁,从而确保在同一时间只有一个线程能够执行被 lock 包围的代码块。

lock (lockObject)
{// 在这里执行需要同步的代码
}

其中,lockObject 是一个用于同步的对象。它可以是任何引用类型的对象,但通常是一个专门用于同步的对象。多个线程可以共享同一个 lockObject,并且只有一个线程能够获得锁并执行被 lock 包围的代码块。

class Program
{static readonly object lockObject = new object(); // 同步对象static void Main(string[] args){for (int i = 0; i < 5; i++){ThreadStart start = () =>{lock (lockObject){Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} is in the critical section.");Thread.Sleep(1000);Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} has exited the critical section.");}};Thread thread = new Thread(start);thread.Start();}Console.ReadKey();}
}

在这里插入图片描述

(2)查看当前工作线程信息

可以使用 Thread.CurrentThread 属性来获取当前正在执行的线程的信息。这个属性返回一个表示当前线程的 Thread 对象,你可以使用它来查询线程的各种属性和状态。

Thread 类还提供了 Priority 属性,允许你设置线程的优先级。然而,操作系统不一定会完全遵循线程的优先级,这取决于操作系统的调度机制。

线程可以分为前台线程和后台线程。前台线程是主线程的一部分,如果所有前台线程都完成,程序将终止。后台线程是在后台运行的线程,如果所有前台线程都完成,程序会立即终止,不会等待后台线程完成。

using System;
using System.Threading;class Program
{static void Main(){Thread currentThread = Thread.CurrentThread;Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");Console.WriteLine($"Thread Name: {currentThread.Name}");Console.WriteLine($"Is Thread Background: {currentThread.IsBackground}");Console.WriteLine($"Thread Priority: {currentThread.Priority}");Console.WriteLine($"Thread State: {currentThread.ThreadState}");}
}

在这里插入图片描述

(3)主线程、前台线程、后台线程

主线程(Main Thread),它是程序的入口点,并且在程序启动时自动创建。主线程负责启动其他线程,并且通常是其他线程的父线程,但并不是所有线程都是主线程的子线程。

线程之间没有严格的父子关系。主线程和其他线程之间通常是平等的,没有直接的父子关系。但是,你可以通过编程来模拟一种线程间的层次关系,使得某些线程在逻辑上看起来是其他线程的子线程。这通常涉及线程的创建、协调和通信

以下是一个示例,演示了如何通过逻辑上的组织来模拟一种主线程和子线程的关系:

using System;
using System.Threading;class Program
{static void Main(){Console.WriteLine("Main thread starts.");Thread parentThread = new Thread(ParentThreadMethod);parentThread.Start();parentThread.Join();Console.WriteLine("Main thread ends.");}static void ParentThreadMethod(){Console.WriteLine("Parent thread starts.");Thread childThread = new Thread(ChildThreadMethod);childThread.Start();childThread.Join();Console.WriteLine("Parent thread ends.");}static void ChildThreadMethod(){Console.WriteLine("Child thread starts.");Thread.Sleep(2000);Console.WriteLine("Child thread ends.");}
}

前台线程(Foreground Threads):
这些线程是由主线程或其他前台线程创建的,它们的生命周期独立于主线程,但它们不是主线程的子线程。前台线程与主线程之间的关系是平级的。当所有前台线程都执行完毕时,程序才会退出,无论主线程是否结束。

  1. 生命周期:
    前台线程的生命周期不受其他线程的影响。即使主线程退出,前台线程仍然可以继续执行,直到完成。
  2. 程序退出:
    如果程序中还有前台线程在运行,主程序将等待所有前台线程完成后才会退出。主线程也是前台线程,如果主线程退出,会等待其他前台线程完成后再退出。
  3. 影响程序:
    前台线程会阻塞程序的退出,直到所有前台线程完成。这可能会影响程序的退出速度。
  4. 默认类型:
    == 通过 new Thread(…) 创建的线程默认是前台线程。==

在这里插入图片描述

后台线程(Background Threads):
这些线程也是由主线程或其他前台线程创建的,它们同样是平级的,不是主线程的子线程。后台线程与主线程之间的关系也是平级的。当所有前台线程结束,程序会退出,同时会终止所有后台线程,不管后台线程是否执行完毕。

  1. 生命周期:
    后台线程的生命周期受到主线程的影响。如果所有前台线程(包括主线程)都已经完成,程序会立即退出,同时终止后台线程,不管后台线程是否执行完毕。
  2. 程序退出:
    如果程序中只剩下后台线程在运行,即使主线程结束,程序也会立即退出,不会等待后台线程完成。
  3. 影响程序:
    后台线程不会阻塞程序的退出,它们对程序的退出速度没有影响。
  4. 设置后台线程:
    可以通过设置线程的 IsBackground 属性为 true 将线程设置为后台线程。通过 Thread 类创建的线程可以使用这个属性进行设置。

在这里插入图片描述

使用场景:

  1. 前台线程通常用于执行一些关键任务,确保这些任务的完成。例如,在主线程需要等待其他线程的结果时,可以使用前台线程。
  2. 后台线程通常用于执行一些非关键性的任务,如日志记录、监控等。它们不会阻止程序的退出,适用于在程序退出时不需要保证任务完全执行的情况。

错误使用后台线程,可能引起资源泄露或意外行为

  1. 资源泄露:
    如果后台线程在程序退出时还在执行,可能会导致资源无法正确释放。例如,如果后台线程打开了文件、网络连接或其他资源,但程序退出时这些资源没有被正确关闭,就会发生资源泄露。
  2. 不完整的操作:
    如果后台线程执行一些需要完整执行的操作,例如数据的写入、状态的更新等,但程序退出时这些操作未完成,可能会导致数据不一致或损坏。
  3. 异常处理:
    后台线程的异常不会被捕获并传播到主线程,可能会导致未处理的异常,影响程序的稳定性。
    4.线程同步:
    在程序退出时,后台线程可能还在等待某些同步操作完成,但这些操作可能无法在后台线程终止之前完成,可能会导致死锁或其他线程同步问题。

1.Thread(线程)

表示一个执行线程,用于并行执行代码。可以使用 Thread 类来创建和管理线程。线程是执行程序的最小单位,多线程编程允许程序同时执行多个任务,从而提高性能和响应性。

Thread 类是 C# 中用于线程操作的基础类之一。然而,对于更高级的线程编程需求,你可能会使用 Task、ThreadPool、异步编程模型等更高级的机制,以便更好地管理和协调多线程操作。

Thead常用方法

  1. Start(): 启动线程,使其开始执行指定的方法。
  2. Join(): 阻塞当前线程,直到目标线程完成。
  3. Abort(): 强制终止线程的执行。不建议使用,因为可能导致资源泄漏或不稳定的状态。
  4. Sleep(int millisecondsTimeout): 使当前线程休眠指定的毫秒数。
  5. IsAlive(): 返回一个布尔值,指示线程是否处于活动状态。
  6. Interrupt(): 中断线程,引发一个 ThreadInterruptedException 异常。
  7. Suspend() 和 Resume(): 已过时,不推荐使用。用于暂停和恢复线程的执行。
  8. GetDomain() 和 GetDomainID(): 获取线程所属的应用程序域和域标识符。
  9. SetApartmentState(ApartmentState state): 设置线程的单元状态,用于控制线程的COM互操作行为。
  10. GetCurrentThreadId() 和 GetDomainID(): 获取当前线程的唯一标识符。
  11. Interrupt(): 中断线程的等待状态,引发 ThreadInterruptedException 异常。
  12. Yield(): 提示系统允许其他等待线程运行。
  13. Name 和 CurrentThread.Name: 获取或设置线程的名称。
  14. SetData 和 GetData: 在线程范围内设置和获取线程本地存储数据。
  15. Start(ParameterizedThreadStart) 和 Start(ParameterizedThreadStart, Object): 启动线程并传递参数给线程方法。
  16. TrySetApartmentState(ApartmentState): 尝试设置线程的单元状态,返回是否成功。
  17. StartNew(Action) 和 StartNew(Action, CancellationToken): 使用 Task 类来启动线程。

这些方法提供了各种线程管理和操作的能力。然而,需要注意,一些方法已经过时,不推荐使用,而且一些方法可能会涉及多线程编程的复杂性,需要谨慎使用。在编写多线程应用程序时,确保仔细阅读文档并根据需求选择适当的方法。

(1)创建线程

通常,你需要传递一个方法作为线程的入口点,然后调用 Start 方法来启动线程。

using System;
using System.Threading;class Program
{static void Main(){Thread thread = new Thread(WorkerMethod);thread.Start(); // 启动线程}static void WorkerMethod(){Console.WriteLine("Thread is running.");}
}

(2) 线程同步

在多线程环境中,线程同步是一种确保多个线程协调工作的机制。Thread 类提供了 Join 方法,允许一个线程等待另一个线程完成。这在需要等待某个线程的结果时特别有用。

using System;
using System.Threading;class Program
{static void Main(){Thread currentThread = Thread.CurrentThread;Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");Thread thread = new Thread(WorkerMethod);thread.Start();// 主线程等待子线程完成thread.Join();Console.WriteLine("Thread has finished.");}static void WorkerMethod(){Thread currentThread = Thread.CurrentThread;Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");Console.WriteLine("Thread is running.");Thread.Sleep(2000); // 模拟耗时操作}
}

在这里插入图片描述

(3)线程异步

using System;
using System.Threading;class Program
{static void Main(){Thread currentThread = Thread.CurrentThread;Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");Thread thread = new Thread(WorkerMethod);thread.Start();// 主线程等待子线程完成//thread.Join();Console.WriteLine("Thread has finished.");//这里子线程虽然还没有处理完,但是直接返回了,没有继续等待子线程,但是子线程还在继续处理工作,没有出现阻塞现象return "ok";}static void WorkerMethod(){Thread currentThread = Thread.CurrentThread;Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");Console.WriteLine("Thread is running.");Thread.Sleep(10000); // 模拟耗时操作//这里在主线程结束后,继续在处理10s后打印Thread is WordEnd;Console.WriteLine("Thread is WordEnd.");}
}

可以思考下,主线程返回成功了,但是子线程执行失败了,这可怎么办?

在这里插入图片描述

2.ThreadPool(线程池)

是一个用于管理和重用线程的机制,可以使用 ThreadPool 类来执行异步任务。

3.Task(任务)

表示一个异步操作,可以使用 Task 类或 Task.Run 方法来创建和管理任务。

(1)Task与Thead的关系

4.Task Parallel Library (TPL)(任务并行库)

是 C# 中用于并行编程的高级库,用于处理异步和并行操作,包括数据并行和任务并行。

5.Async/Await(异步/等待)

是 C# 5.0 引入的异步编程模型,用于创建和管理异步方法和操作。

6.Monitor(监视器)

是用于实现线程同步的一种机制,用于保护共享资源,避免竞态条件。可以使用 Monitor 类或 lock 关键字来实现。

首先lock和Minitor有什么区别呢?
其实lock在IL代码中会被翻译成Monitor。也就是Monitor.Enter(obj)和Monitor.Exit(obj).

lock(obj)
{}//等价为:
try{    Monitor.Enter(obj)
}catch()
{}
finally
{Monitor.Exit(obj)
}

7.Semaphore(信号量)

用于控制并发访问资源的数量,可以使用 Semaphore 类来创建和管理信号量。

8.SemaphoreSlim

是 Semaphore 的改进版本,提供更好的性能和可伸缩性。

9.AutoResetEvent(自动复位事件)

用于线程同步,允许一个线程等待另一个线程发出信号。

10.ManualResetEvent(手动复位事件)

用于线程同步,允许一个线程等待多个线程发出信号。

11.CancellationToken(取消标记)

用于在异步操作中请求取消操作,可以在异步方法中传递给取消标记。

12.volatile(易失性修饰符)

用于标记字段,指示编译器不应该对标记字段进行优化,以确保多线程环境下的正确性。

13.Mutex(互斥锁)

是一种用于实现线程同步的机制,用于保护共享资源,防止多个线程同时访问。

14.ReaderWriterLock(读写锁)

允许多个线程同时读取共享资源,但只允许一个线程写入资源。适用于读操作频繁、写操作较少的场景。

15.ReaderWriterLockSlim(轻量级读写锁)

是 ReaderWriterLock 的改进版本,提供更好的性能和可伸缩性。

16.SpinLock

是一种自旋锁,用于短时间内的临界区保护。它使用忙等待来尝试获取锁,适用于临界区很小的情况。

17.SpinWait

用于在自旋等待期间执行自旋操作,可以根据不同的条件进行自旋。

18.Barrier(屏障)

允许多个线程在一个点上等待,直到所有线程都达到该点。适用于需要所有线程协调同步的场景。

四、多线程的异常捕获问题

相关文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/35656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NanoPi NEO移植LVGL8.3.5到1.69寸ST7789V屏幕

移植前准备 移植好fbtft屏幕驱动 参考链接&#xff1a;友善之臂NanoPi NEO利用fbtft驱动点亮1.69寸ST7789V2屏幕 获取源码 名称地址描述lvglhttps://github.com/lvgl/lvgl.gitlvgl-8.3.5lv_drivershttps://github.com/lvgl/lv_drivers.gitlv_drivers-6.1.1 创建工程目录 创…

旋转图像(旋转矩阵)

原题链接 旋转图像备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/rotate-image/ 算法分析 若矩阵的行列数为N&#xff0c;设i表示行索引&#xff0c;i属…

win10中Docker安装、构建镜像、创建容器、Vscode连接实例

Docker方便一键构建项目所需的运行环境&#xff1a;首先构建镜像(Image)。然后镜像实例化成为容器(Container)&#xff0c;构成项目的运行环境。最后Vscode连接容器&#xff0c;方便我们在本地进行开发。下面以一个简单的例子介绍在win10中实现&#xff1a;Docker安装、构建镜像…

Flutter BottomSheet 三段式拖拽

BottomSheetBehavior 追踪 BottomSheet系统默认实现效果准备要实现的功能点&#xff1a;定义三段式状态&#xff1a;BottomSheetBehavoir阀值定义1. 未达到滚动阀值&#xff0c;恢复状态2. 达到滚动阀值&#xff0c;更新状态 前面倒是有讲过Android原生的BottomSheetBehavior&a…

Flask 框架集成Bootstrap

前面学习了 Flask 框架的基本用法&#xff0c;以及模板引擎 Jinja2&#xff0c;按理说可以开始自己的 Web 之旅了&#xff0c;不过在启程之前&#xff0c;还有个重要的武器需要了解一下&#xff0c;就是著名的 Bootstrap 框架和 Flask 的结合&#xff0c;这将大大提高开发 Web …

国产数据库-内核特性-低基数全局字典

国产数据库-内核特性-StarRocks低基数全局字典 StarRocks2.0引入了低基数全局字典&#xff0c;可以通过全局字典将字符串的相关操作转换成整型相关操作&#xff0c;大大提升查询性能。 1、低基数字典 对于利用整型替代字符串进行处理&#xff0c;通常使用字典编码进行优化。Sta…

人大金仓助力某大型金融机构业务系统异地容灾优化升级

日前&#xff0c;人大金仓助力某大型金融机构应收账款融资服务平台异地容灾项目顺利上线&#xff0c;保证了平台系统运行的连续性和数据安全&#xff0c;为充分发挥平台的融资功能&#xff0c;缓解中小微企业融资难提供了强有力的保障。 “ 缓解中小微企业融资难 某大型金融机构…

【MySQL--->数据库操作】

文章目录 [TOC](文章目录) 一、操作语句1.增2.删3.改4.查5.备份 二、字符集与校验规则 一、操作语句 1.增 语句格式:create database [if no exists]数据库名[create_specification [,create_specification] …]; 中括号内是可选项,if no exists是指如果数据库不存在就创建,存…

STM32 F103C8T6学习笔记7:双机无线串口通信

今日尝试配通俩个C8T6单片机之间的无线串口通信&#xff0c;文章提供原理&#xff0c;源码&#xff0c;测试效果图&#xff0c;测试工程下载&#xff1a; 目录 传输不规范问题&#xff1a; 串口通信资源&#xff1a; 单个串口资源理解&#xff1a; 单片机串口资源&#xf…

Redis的单线程与多线程

Redis的核心处理逻辑一直都是单线程 有一些分支模块是多线程(某些异步流程从4.0开始用的多线程&#xff0c;例如UNLINK、FLUSHALL ASYNC、FLUSHDB ASYNC等非阻塞的删除操作。网络I/O解包从6.0开始用的是多线程;) 为什么是单线程 多线程多好啊可以利用多核优势 官方给的解释 …

UI自动化环境的搭建(python+pycharm+selenium+chrome)

最近在做一些UI自动化的项目&#xff0c;为此从环境搭建来从0到1&#xff0c;希望能够帮助到你&#xff0c;同时也是自我的梳理。将按照如下进行开展&#xff1a; 1、python的下载、安装&#xff0c;python环境变量的配置。 2、pycharm开发工具的下载安装。 3、selenium的安装。…

Leetcode34 在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 代码&#xff1a; c…

服务器安全维护注意事项有哪些?

服务器的安全关系着公司整个网络以及所有数据的安全&#xff0c;我们该如何做好服务器后续的安全维护呢?河南亿恩科技股份有限公司&#xff0c;专注服务器托管23年&#xff0c;不仅是国内专业的互联网基础应用服务提供商之一&#xff0c;还是国家工信部认定的综合电信服务运营…

【Linux命令行与Shell脚本编程】第十九章 正则表达式

Linux命令行与Shell脚本编程 第十九章 正则表达式 文章目录 Linux命令行与Shell脚本编程 第十九章 正则表达式九.正则表达式9.1.正则表达式基础9.1.1.正则表达式的类型9.2.定义BRE模式9.2.1.普通文本9.2.2.特殊字符 9.2.3.锚点字符锚定行首^锚定行尾$组合锚点 9.2.4.点号字符\.…

funbox3靶场渗透笔记

funbox3靶场渗透笔记 靶机地址 https://download.vulnhub.com/funbox/Funbox3.ova 信息收集 fscan找主机ip192.168.177.199 .\fscan64.exe -h 192.168.177.0/24___ _/ _ \ ___ ___ _ __ __ _ ___| | __/ /_\/____/ __|/ __| __/ _ |/ …

SpringBoot复习(39)Servlet容器的自动配置原理

Servlet容器自动配置类为ServletWebServerFactoryAutoConfiguration 可以看到通过Import注解导入了三个配置类&#xff1a; 通过这个这三个配置类可以看出&#xff0c;它们都使用了ConditionalOnClass注解&#xff0c;当类路径存在tomcat相关的类时&#xff0c;会配置一个T…

Linux系列:从0到1用Docker部署springboot项目

目录 1.前提条件 2.编写DockerFile镜像文件 3.打包SpringBoot项目 4.通过软件Xftp进行传输&#xff08;*&#xff09; 1.点击“文件-新建”​编辑 5.操作远程主机 1.docker构建 2.容器运行 6.容器的关闭和删除 1.前提条件 Linux、docker、xftp的安装、一台可以访问的远…

教雅川学缠论07-中枢实战众泰汽车000980

本文实战众泰汽车 下面是2023年11月14-2023年8月8众泰汽车日K图 先画日K 接下来处理包含&#xff0c;就变成下面这个样子 下面在套上缠论的理论&#xff0c;未来股价的走势应该是红色椭圆形虚线里面的样子 好了&#xff0c;文章就到这里&#xff0c;如果众泰最终不是这个走势…

IDEA部署配置Maven项目教程,IDEA配置Tomcat(2019.3.3)

一、前言 当涉及到软件开发和项目管理时&#xff0c;使用一个可靠的构建工具是非常重要的。Maven是一个广泛使用的构建工具&#xff0c;它为Java项目提供了一种简化的构建过程和依赖管理。 在本文中&#xff0c;我们将探讨如何部署Maven并开始使用它来构建您的项目。我们将介绍…

开源数据库Mysql_DBA运维实战 (修改root密码)

MySQL——修改root密码的4种方法 本文以windows为例为大家详细介绍下MySQL修改root密码的4种方法&#xff0c;大家可以可以根据的自己的情况自由选择&#xff0c;希望对大家有所帮助 方法1&#xff1a; 用SET PASSWORD命令 首先登录MySQL。 格式&#xff1a;mysql> set pass…