一台服务器能运行多少个线程,大致取决于CPU的管理能力。CPU负责线程的创建、协调、切换、销毁、暂停、唤醒、运行等。
一个应用程序中,必须有一个进程维持应用程序的运行环境,一个进程可同时有多个线程协作处理应用逻辑。

同步:单线程,每一步都执行结束并返回结果,下一步处于等待,阻塞程序流

异步:多线程,不需要等待执行结束,可继续执行下一步,形成并行处理,无序的不可预测的执行顺序

前台线程:主线程退出后,子线程直至完成计算。

后台线程:主线程退出后,子线程也会停止退出。

作者:[Sol·wang] - 博客园,原文出处:https://www.cnblogs.com/Sol-wang/p/14793008.html

一、线程的应用

常见的线程应用方式

  • new Thread
  • ThreadPool.QueueUserWorkItem 后台线程
  • Task.Run / Task.Factory.StartNewd
  • Parallel
  • await / async

ThreadPool 线程池

线程池线程是后台线程。 每个线程均使用默认的堆栈大小,以默认的优先级运行,并且位于多线程单元中。 一旦线程池中的线程完成任务,它将返回到等待线程队列中。 这时开始即可重用它。 通过这种重复使用,应用程序可以避免产生为每个任务创建新线程的开销。

每个进程只有一个线程池。由线程池统一管理每个线程的创建/分配/销毁。

// 设置可同时并行运行的线程数
ThreadPool.SetMinThreads
ThreadPool.SetMaxThreads
// 用线程池中的后台线程执行方法
ThreadPool.QueueUserWorkItem(new WaitCallback(方法名), 参数);

Task

Task所用到的线程同样来自于ThreadPool。所以通过ThreadPool线程数量的设置有助于Task的线程管理。

// Action:有参    (Action 与 Func 的区别:Action不能有返回值,Func必须有返回值)
Action<string> action = (string a) =>
{
Console.WriteLine($"有参Action\tparams {a}\tTId {Thread.CurrentThread.ManagedThreadId}");
}; // Create a task
Task t1 = new Task(action, "alpha");
t1.Start(); // Task.Factory
Task t2 = Task.Factory.StartNew(action, "beta");
t2.Start(); // Task 的返回值(等待返回结果)
Task<int> task = new Task<int>(() =>
{
//...
return 0;
});
task.Start();
int result = task.Result; // 仅仅启动一个Task
Task.Run(() =>
{
//...
}); // 批量启动 Task
// 同时并行运行至少30个线程的方式
ThreadPool.SetMinThreads(30, 50);
ThreadPool.SetMaxThreads(50, 70);
List<Task> tks = new List<Task>();
for (int i = 0; i < 30; i++)
{
tks.Add(Task.Run(() =>
{
//...
}));
}
// 等待线程全部运行结束
Task.WaitAll(tks.ToArray()); // 取消线程运行
// 更多取消方式参考 https://learn.microsoft.com/zh-cn/dotnet/standard/threading/cancellation-in-managed-threads
CancellationTokenSource cts = new CancellationTokenSource();
Task.Run(() =>
{
if (Console.ReadKey(true).KeyChar.ToString().ToUpperInvariant() == "C")
{
// 取消
cts.Cancel();
}
});

Parallel

多线程并行处理的Parallel.InvokeParallel.ForParallel.ForEach;无法确保顺序处理。

// 示例:并行运行几个方法
Parallel.Invoke(方法1, 方法2, () => 方法3, () => 方法4) // ParallelOptions 参数设定
// 先指定启用30个线程同时处理的设置
ParallelOptions paroptions = new ParallelOptions();
paroptions.MaxDegreeOfParallelism = 30; // 指定数量的线程,并行执行200遍
List<int> datas = new List<int>();
Parallel.For(0, 200, paroptions, index =>
{
datas.add(index);
}); // 指定数量的线程,并行读取集合数据
Parallel.ForEach(datas, paroptions, (item) =>
{
Console.WriteLine(item);
});

await / async

创建后台线程并行处理,并取得处理结果。

// 执行 async 方法(后台子线程执行)
Task<string> _task_result_1 = t1.Func1();
// 主线程不等返回结果,继续往下执行 // 继续执行方法
string _result_2 = t1.Func2();
// 以上两个方法 Func1()、Func2() 并行执行 // 取 Func1 的运行结果
string _result_1 = await _task_result_1; // 整合两个方法的运行结果
int _total_ = _result_1.length + _result_2.length;

async 定义方法有返回值时用Task<T> 。
用 await 调用 async 定义的方法时,多线程,阻塞式同步执行。
不用 await 调用 async 定义的方法时,多线程,非阻塞异步执行。

二、常用的线程锁

lock

通用的标准锁,封装自应用级锁Monitor类,需要有线程共享的锁标识,告知其它线程是否等待。

Monitor

程序级锁,于单个应用范围内,通过获取或释放用于标识资源 T 来授予对共享资源的相互独占访问权限。

// 给要操作的对象加把锁,排他锁
Monitor.Enter(T);
// 释放当前锁,允许其它线程使用了
Monitor.Exit(T);

SpinLock

快速的、低级别的简易锁。不同于标准锁,适合应用于简单的、非耗时的逻辑处理,此场景下更多时候性能优于标准锁。

如果应用场景并非足够简单或存在不确定性的可能,SpinLock 将比标准锁开销更大。

static SpinLock _spinlock = new SpinLock();

bool lockTaken = false;
try
{
// 加锁
_spinlock.Enter(ref lockTaken);
// ...
}
finally
{
// 释放
if (lockTaken) _spinlock.Exit(false);
}

Interlocked

更细化的锁,针对性的场景时用来代替lock,包括非空、递增等针对场景的应用,性能优于lock

// 递增场景案例

// lock 方式
lock(lockObject)
{
myField++;
} // Interlocked 的替代方式
System.Threading.Interlocked.Increment(myField);

Semaphore

进程间同步,跨应用限制可同时访问某一资源或资源池的线程数,允许同时共享的线程数

// 创建时,定义同时的默认线程数和最多线程数(起始线程数,最多线程数)
Semaphore sem = new Semaphore(10, 20);
// 上锁
sem.WaitOne();
// 释放锁(一次释放线程数)
sem.Release(3);

应用场景:假如多线程写入DB,当给DB类上锁时,可结合DB的max_connection值,设置允许同时访问的线程数。

Mutex

进程间同步,系统级锁,所以可跨进程跨应用。
// 阻挡锁住当前块,其它线程处于等待
// 超时后返回false,不允许其它线程进入
Mutex.WaitOne(timeout);
// 释放当前,一个,允许其它线程进入
Mutex.ReleaseMutex();

三、线程安全

当多个线程访问同一个对象的属性或方法时,对这些调用进行同步处理是非常重要的。如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步或者在调用方进行任何其它的协调操作,调用这个对象的行为都可以获得有效的结果,其成员不受中断影响的类,那这个对象是线程安全的。否则,一个线程可能会中断另一个线程正在执行的任务,可能使该对象处于无效状态。

通常多个线程在调用同一共享资源,为了解决线程间的同步和互相干扰中断,会对共享资源加锁,使其线程间有序的独占运行。

在早期的.NET版本中,提供了常用的线程安全类,通过Synchronized方法创建的实例实现线程同步。
如:ArrayList,Hashtable

// Synchronized 方法案例

// 线程安全创建对象,并实现多线程同步
Hashtable ht = Hashtable.Synchronized(new Hashtable());
// 多线程调用 不用锁
ht.Add("key1", true);

线程安全的高性能集合类

.NET Framework 4 引入了几种专为支持多线程添加和删除操作而设计的集合类型。 为了实现线程安全,这些类型使用多种高效的锁定和免锁定同步机制。 同步会增加操作的开销。 开销数取决于所用的同步类型、执行的操作类型和其他因素,例如尝试并行访问该集合的线程数。

System.Collections.Concurrent 命名空间,其中包含多个线程安全且可缩放的集合类。 多个线程可以安全高效地从这些集合添加或删除项,而无需在用户代码中进行其他同步。 编写新代码时,只要将多个线程同时写入到集合时,就使用并发集合类。

直接在其它线程中对并发集合操作,不需要手动加锁:

ConcurrentBag  无序元素集合的线程安全实现

ConcurrentStack  LIFO(后进先出)堆栈的线程安全实现

ConcurrentQueue  FIFO(先进先出)队列的线程安全实现

ConcurrentDictionary  键值对字典的线程安全实现

.Net 线程与锁的更多相关文章

  1. JAVA语言规范-线程和锁章节之同步、等待和通知

    JAVA语言规范:线程和锁 1 同步 java编程语言提供了线程间通信的多种机制.这些方法中最基本的是同步化,此方法是使用监视器实现的.JAVA中每个对象与一个监视器相关联,一个线程可以加锁和解锁监视 ...

  2. GIL与线程互斥锁

    GIL 是解释器级别的锁,是限制只有一个原生线程运行,防止多个原生线程之间修改底层的共享数据.而线程互斥锁是防止多个线程同时修改python内存空间的共享数据.

  3. Java线程:锁

    一.锁的原理 Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行的代码类的当前实例(this实例)有关的锁.获得一个对象的锁也称为获取锁.锁 ...

  4. (三)juc高级特性——虚假唤醒 / Condition / 按序交替 / ReadWriteLock / 线程八锁

    8. 生产者消费者案例-虚假唤醒 参考下面生产者消费者案例: /* * 生产者和消费者案例 */ public class TestProductorAndConsumer { public stat ...

  5. Java 线程与锁

    Synchronization synchronized语法可以获取锁, 当其他线程持有锁的时候该线程想要获取锁将会进入等待状态, 直到没有其他线程持有该锁 显示使用 synchronized (lo ...

  6. Java线程与锁

    概要:线程的实现方法. 线程调度.线程状态及转换.线程安全(5种分类.3种实现方法.锁优化技术) 进程是OS进行资源分配的基本单位,线程是CPU调度的基本单位. 1.线程的实现方法 可参阅 我是一个进 ...

  7. java多线程 -- 线程八锁

    一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些 ...

  8. GUC-10 线程八锁

    /* * 题目:判断打印的 "one" or "two" ? * * 1. 两个普通同步方法,两个线程,标准打印, 打印? //one two * 2. 新增 ...

  9. JUC——线程同步锁(锁处理机制简介)

    锁处理机制简介 juc的开发框架解决的核心问题是并发访问和数据安全操作问题,当进行并发访问的时候如果对于锁的控制不当,就会造成死锁这样的阻塞问题. 为了解决这样的缺陷,juc里面重新针对于锁的概念进行 ...

  10. GIL线程全局锁 协程

    GIL线程全局锁 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务 ...

随机推荐

  1. MicroNet: 低秩近似分解卷积以及超强激活函数,碾压MobileNet | 2020新文分析

    论文提出应对极低计算量场景的轻量级网络MicroNet,包含两个核心思路Micro-Factorized convolution和Dynamic Shift-Max,Micro-Factorized ...

  2. KingbaseES V8R6 表空间加密

    透明存储加密优势 透明存储加密可确保加密敏感数据满足合规性要求,并提供简化加密操作的功能,优势如下: 作为安全管理员,您可以确保敏感数据已加密,因此在存储介质或数据文件被盗或入侵者试图从操作系统访问数 ...

  3. jQuery获得或设置内容和属性、添加属性 append和after的区别

    来自w3school 在线教程 jQuery获得或设置内容和属性 text() - 设置或返回所选元素的文本内容 html() - 设置或返回所选元素的内容(包括 HTML 标记) val() - 设 ...

  4. Python爬虫爬取ECCV Conference Papers(一)

    爬取到2020年所有论文标题 代码: 1 import re 2 import requests 3 from bs4 import BeautifulSoup 4 import lxml 5 imp ...

  5. #网络流,dinic,最小割#洛谷 3227 [HNOI2013]切糕

    题目传送门 题目大意 \(P\)行\(Q\)列的楼房高度均为\(R\),每一层改造要花费一定的金钱, 每个楼房都要挑选有且仅有一层进行改造,并且相邻两个楼房改造位置的相对高度不能超过\(D\), 问最 ...

  6. 在DAYU200上实现OpenHarmony视频播放器

    内容简介 本文介绍了如何使用ArkUI框架提供的video组件,实现一个具有简易播放器.通过VideoController控制器来控制倍速.全屏.进度调节等功能. 由于使用本地视频文件会影响App的包 ...

  7. openGauss关于PL/SQL匿名块调用测试

    openGauss 关于 PL/SQL 匿名块调用测试 一.原理介绍 PL/SQL(Procedure Language/Structure Query Language)是标准 SQL 语言添加了过 ...

  8. HarmonyOS—UI 开发性能提升的推荐方法

    注:本文转载自 HarmonyOS 官网文档 开发者若使用低性能的代码实现功能场景可能不会影响应用的正常运行,但却会对应用的性能造成负面影响.本章节列举出了一些可提升性能的场景供开发者参考,以避免应用 ...

  9. HarmonyOS智能座舱体验是怎样炼成的?立即查看

    原文链接:https://mp.weixin.qq.com/s/AGY2hAeXngtRrZFk0FXe5g,点击链接查看更多技术内容: 随着AITO问界M5热卖以及M7.M5 EV版本的陆续交付,A ...

  10. http协议重新整理——————历史[一]

    前言 简单整理一些http协议. 正文 20 世纪 60 年代,美国国防部高等研究计划署(ARPA)建立了 ARPA 网,它有四个分布在各地的节点,被认为是如今互联网的"始祖". ...