You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MultiThreadingStudy/Docs/Jupyter笔记.2.5.原子操作.ipynb

699 lines
20 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"原子操作\n",
"======="
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"在多线程环境中,因为多个线程可能会同时访问同一个资源,我们需要使用一些方式保证访问不发生冲突,其中最基础的方式就是原子操作。 \n",
"原子操作是利用 CPU 提供的特性,所以不会阻塞线程。 \n",
"线程安全方式有: \n",
"+ 使用设计,避免多线程中的资源共享\n",
"+ 利用线程本地存储,使资源复制到线程本地,实现互相独立、互不干扰\n",
"+ 使用原子操作\n",
"+ 使用锁\n",
"+ 使用信号量\n",
"+ 综合使用"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 全局设置语言设置、Nuget包引用、空间引用等"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"//全局设置\n",
"#!csharp\n",
"using System.Threading;\n",
"using System.Threading.Channels;\n",
"using System.Threading.Tasks;\n",
"\n",
"//全局变量\n",
"var noteBookThreadDesc = \"NoteBook线程\";\n",
"\n",
"//全局方法\n",
"//显示线程信息\n",
"public void ShowThreadInfo(Thread showThread=null, string describe = null)\n",
"{\n",
" if(showThread == null)\n",
" {\n",
" showThread = Thread.CurrentThread;\n",
" }\n",
"\n",
" if(string.IsNullOrWhiteSpace(describe))\n",
" {\n",
" describe = showThread.Name == null ? \"无名\" : showThread.Name;\n",
" }\n",
"\n",
" Console.WriteLine($\"{describe}线程ID{showThread.ManagedThreadId} \");\n",
" Console.WriteLine($\"{describe}线程名:{showThread.Name} \");\n",
" Console.WriteLine($\"{describe}线程状态:{showThread.ThreadState} \");\n",
" Console.WriteLine($\"{describe}线程模式:{showThread.GetApartmentState()} \");\n",
" Console.WriteLine($\"{describe}激活:{(showThread.IsAlive ? \"活动\" : \"非活动\")} \");\n",
" Console.WriteLine($\"{describe}线程池线程:{(showThread.IsThreadPoolThread ? \"是的\" : \"否\")} \");\n",
" Console.WriteLine($\"{describe}后台线:{(showThread.IsBackground ? \"是的\" : \"不是\")} \");\n",
" Console.WriteLine($\"{describe}区域:{showThread.CurrentCulture}\");\n",
" Console.WriteLine($\"{describe}UI区域{showThread.CurrentUICulture}\");\n",
" Console.WriteLine($\"{describe}优先级:{showThread.Priority}\");\n",
"}\n",
"\n",
"//显示线程状态\n",
"public void ShowThreadState(Thread showThread=null, string describe = null)\n",
"{\n",
" if(showThread == null)\n",
" {\n",
" showThread = Thread.CurrentThread;\n",
" }\n",
"\n",
" if(string.IsNullOrWhiteSpace(describe))\n",
" {\n",
" describe = showThread.Name == null ? \"无名\" : showThread.Name;\n",
" }\n",
" Console.WriteLine($\"{describe}线程状态:{showThread.ThreadState} \");\n",
"}"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## .Net中的原子操作类"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
".Net中 System.Threading.Interlocked 类提供了用于执行原子操作的函数,这些函数接收引用参数(ref),也就是变量内存地址,然后针对该内存地址的值执行原子操作。"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### 原子递增 Interlocked.Increment()\n",
"Interlocked.Increment 函数执行的原子操作属于 “获取-添加”分类执行后变量的值增加1返回的值是增加后的值即增加前的值加1"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"//计数器类\n",
"public class Counter\n",
"{\n",
" //总次数\n",
" public static int TotalNumber = 0;\n",
" \n",
" //方法循环次数\n",
" public static readonly int LoopNumber = 100;\n",
"\n",
" //执行\n",
" public static void Execute()\n",
" {\n",
" for (int i = 1; i <= LoopNumber; i++)\n",
" {\n",
" //原子操作\n",
" System.Threading.Interlocked.Increment(ref TotalNumber);\n",
" }\n",
" Console.WriteLine($\"线程[{Thread.CurrentThread.ManagedThreadId.ToString(\"000\")}] 执行了累加 {LoopNumber} 次,结束时 TotalNumber = {TotalNumber}\");\n",
" }\n",
"}\n",
"\n",
"//使用域隔离和代码折叠\n",
"{\n",
" var threads = new List<Thread>()\n",
" {\n",
" new Thread(Counter.Execute){Name=\"thread_a\"},\n",
" new Thread(Counter.Execute){Name=\"thread_b\"}\n",
" };\n",
"\n",
" threads.ForEach(t => t.Start());\n",
" threads.ForEach(t => t.Join());\n",
"\n",
" //主线程信息\n",
" Console.WriteLine($\"主(执行)线程[{Thread.CurrentThread.ManagedThreadId.ToString(\"000\")}] 结束时TotalLoopNumber = {Counter.TotalNumber}\");\n",
"}\n",
"\n",
"//原子操作,多线程下多次执行结果相同"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 原子递减 Interlocked.Decrement()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"属于 `获取-添加`分类执行后变量的值减少1返回值是减少后的值即原值减1"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#region 原子递减\n",
"//原子递减\n",
"public class Counter4Decrement\n",
"{\n",
" //总次数\n",
" public static int TotalNumber = 0;\n",
" \n",
" //方法循环次数\n",
" public static readonly int LoopNumber = 10000;\n",
"\n",
" //执行\n",
" public static void Decrement()\n",
" {\n",
" for (int i = 1; i <= LoopNumber; i++)\n",
" {\n",
" //原子操作:循环递减,返回递减后的值\n",
" var decreValue = System.Threading.Interlocked.Decrement(ref TotalNumber);\n",
" }\n",
" Console.WriteLine($\"线程[{Thread.CurrentThread.Name}] 执行了递减 {LoopNumber} 次,结束时 TotalNumber = {TotalNumber}\");\n",
" }\n",
"}\n",
"\n",
"//使用域隔离和代码折叠\n",
"{\n",
" var threads = new List<Thread>()\n",
" {\n",
" new Thread(Counter4Decrement.Decrement){Name=\"thread_a\"},\n",
" new Thread(Counter4Decrement.Decrement){Name=\"thread_b\"}\n",
" };\n",
"\n",
" threads.ForEach(t => t.Start());\n",
" threads.ForEach(t => t.Join());\n",
"\n",
" //主线程信息\n",
" Console.WriteLine($\"主(执行)线程[{Thread.CurrentThread.Name}] 结束时TotalLoopNumber = {Counter4Decrement.TotalNumber}\");\n",
"}\n",
"\n",
"//原子操作:\n",
"//1、多线程下,每线程执行结束后共享变量可能不同\n",
"//2、所有线程结束, 主线程获取的变量值与最后完成的线程值相同\n",
"#endregion"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 原子增加指定数量(负数为减) Interlocked.Add()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Interlocked.Add 属于 `获取-添加`分类,执行后变量的值加等于第二个参数的值,返回值是增加后的值即原值加第二个参数值"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"//原子增加指定数量\n",
"public class Counter4Add\n",
"{\n",
" //共享变量\n",
" public static int ShareNumber = 0;\n",
"\n",
" //执行\n",
" public static void Add()\n",
" {\n",
" //随机休眠,打乱多线程执行顺序\n",
" Thread.Sleep(Random.Shared.Next(100,500));\n",
"\n",
" //原子操作:增加指定数量\n",
" var decreValue = System.Threading.Interlocked.Add(ref ShareNumber,2);\n",
" Console.WriteLine($\"线程[{Thread.CurrentThread.Name}] 递减2后 ShareNumber = {ShareNumber}\");\n",
" }\n",
"}\n",
"\n",
"//使用域隔离和代码折叠\n",
"{\n",
" var threads = new List<Thread>()\n",
" {\n",
" new Thread(Counter4Add.Add){Name=\"thread_a\"},\n",
" new Thread(Counter4Add.Add){Name=\"thread_b\"}\n",
" };\n",
"\n",
" threads.ForEach(t => t.Start());\n",
" threads.ForEach(t => t.Join());\n",
"\n",
" //主线程信息\n",
" Console.WriteLine($\"主[执行]线程获共享变量 ShareNumber 最终的值 = {Counter4Add.ShareNumber}\");\n",
"}\n",
"\n",
"//原子操作:\n",
"//1、两个线程同时执行会输出: 2 4 或 4 2\n",
"//2、全局变量最终的值为 4"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 原子替换 Interlocked.Exchange()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Interlocked.Exchange() 属于 `测试-设置` 分类, 将变量的值修改为第二个参数的值,返回修改前的值"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"//原子替换\n",
"public class Counter4Exchange\n",
"{\n",
" //共享变量\n",
" public static int ShareNumber = 0;\n",
"\n",
" //执行\n",
" public static void Exchange()\n",
" {\n",
" //随机休眠,打乱多线程执行顺序\n",
" Thread.Sleep(Random.Shared.Next(100,500));\n",
"\n",
" //原子操作:替换\n",
" var oldValue = System.Threading.Interlocked.Exchange(ref ShareNumber,1);\n",
" Console.WriteLine($\"线程[{Thread.CurrentThread.Name}] 替换前的值为 {oldValue}\");\n",
" }\n",
"}\n",
"\n",
"//使用域隔离和代码折叠\n",
"{\n",
" var threads = new List<Thread>()\n",
" {\n",
" new Thread(Counter4Exchange.Exchange){Name=\"thread_a\"},\n",
" new Thread(Counter4Exchange.Exchange){Name=\"thread_b\"}\n",
" };\n",
"\n",
" threads.ForEach(t => t.Start());\n",
" threads.ForEach(t => t.Join());\n",
"\n",
" //主线程信息\n",
" Console.WriteLine($\"主[执行]线程获共享变量 ShareNumber 最终的值 = {Counter4Exchange.ShareNumber}\");\n",
"}\n",
"\n",
"//原子替换:\n",
"//1、两个线程同时执行会输出: 0 1 或 1 0\n",
"//2、全局变量最终的值为 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 原子比较替换 Interlocked.CompareExchange()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Interlocked.CompareExchange() 属于 `比较-交换` 分类, 执行时如果变量的值等于第三个变量的值,则修改为第二个变量的值并返回修改前的值;否则直接返回现有值"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"//原子比较替换\n",
"public class Counter4CompareExchange\n",
"{\n",
" //共享变量\n",
" public static int ShareNumber = 0;\n",
"\n",
" //执行\n",
" public static void CompareExchange()\n",
" {\n",
" //随机休眠,打乱多线程执行顺序\n",
" Thread.Sleep(Random.Shared.Next(100,500));\n",
"\n",
" //原子操作:比较替换\n",
" int y;\n",
" while(true)\n",
" {\n",
" y = ShareNumber;\n",
" int oldShareValue = System.Threading.Interlocked.CompareExchange(ref ShareNumber,y+1,y);\n",
"\n",
" if(oldShareValue == y)\n",
" {\n",
" break;\n",
" }\n",
" }\n",
"\n",
" Console.WriteLine($\"线程[{Thread.CurrentThread.Name}] 替换前的值为 {y+1}\");\n",
" }\n",
"}\n",
"\n",
"//使用域隔离和代码折叠\n",
"{\n",
" var threads = new List<Thread>()\n",
" {\n",
" new Thread(Counter4CompareExchange.CompareExchange){Name=\"thread_a\"},\n",
" new Thread(Counter4CompareExchange.CompareExchange){Name=\"thread_b\"}\n",
" };\n",
"\n",
" threads.ForEach(t => t.Start());\n",
" threads.ForEach(t => t.Join());\n",
"\n",
" //主线程信息\n",
" Console.WriteLine($\"主[执行]线程获共享变量 ShareNumber 最终的值 = {Counter4CompareExchange.ShareNumber}\");\n",
"}\n",
"\n",
"//原子替换:\n",
"//1、两个线程同时执行会输出: 1 2 或 2 1\n",
"//2、全局变量最终的值为 2"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 原子读取 Interlocked.Read()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"从内存读取值通常都是原子的除非32位平台读取64位数据比如32位平台上读取 long 型数值。因为32位平台读取64位数据是分高位和低位两次读取操作的所以不是原子操作。原子读取一般会与其它原子操作配合使用。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"//原子读取\n",
"public class Counter4Read\n",
"{\n",
" //共享变量,64位数据\n",
" public static long ShareNumber = 0;\n",
"\n",
" //循环次数\n",
" public readonly static int LoopNumber = 10000;\n",
"\n",
" //一个线程循环替换数据\n",
" public static void Exchange()\n",
" {\n",
" for(int i=0; i< LoopNumber; i++)\n",
" {\n",
" System.Threading.Interlocked.Exchange(ref ShareNumber,0x7aaabbbbccccdddd);\n",
" }\n",
" }\n",
" \n",
" //一个线程循环读取\n",
" //无论多少次读取的都是 0 或者 0x7aaabbbbccccdddd\n",
" //不可能是 0x7aaabbbb00000000 或者 ccccdddd\n",
" public static void Read()\n",
" {\n",
" for(int i=0; i< LoopNumber; i++)\n",
" {\n",
" long y = System.Threading.Interlocked.Read(ref ShareNumber);\n",
"\n",
" //此条件应该永远不成立\n",
" if(y==0x7aaabbbb00000000 || y==0x00000000ccccdddd)\n",
" { \n",
" Console.WriteLine($\"线程[{System.Threading.Thread.CurrentThread.Name}], 读取到非原子值{y}\");\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"//使用域隔离和代码折叠\n",
"{\n",
" var threads = new List<Thread>()\n",
" {\n",
" new Thread(Counter4Read.Exchange){Name=\"thread_a\"},\n",
" new Thread(Counter4Read.Read){Name=\"thread_b\"}\n",
" };\n",
"\n",
" threads.ForEach(t => t.Start());\n",
" threads.ForEach(t => t.Join());\n",
"\n",
" //主线程信息\n",
" Console.WriteLine($\"主[执行]线程获共享变量 ShareNumber 最终的值 = {Counter4Read.ShareNumber}\");\n",
"}\n",
"\n",
"//原子读取:\n",
"//1、无论多少次读取的都是 0 或者 0x7aaabbbbccccdddd 不可能是 0x7aaabbbb00000000 或者 ccccdddd\n",
"//2、全局变量最终的值为 0x7aaabbbbccccdddd 即是 8839083633937276381 或者极概率是 0"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 内存屏障 Interlocked.MemoryBarrier()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"System.Threading.Interlocked 还提供了一些其它方法,比如内存屏障相关的 MemoryBarrier() 和 MemoryBarrierProcessWide() 和 新的 And() Or()方法等。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"/*\n",
"System.Threading.Interlocked.MemoryBarrier();\n",
"System.Threading.Interlocked.MemoryBarrierProcessWide();\n",
"System.Threading.Interlocked.And();\n",
"System.Threading.Interlocked.Or();\n",
"*/"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "11.0"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": ".NET"
},
{
"aliases": [
"C#",
"c#"
],
"languageName": "C#",
"name": "csharp"
},
{
"aliases": [
"F#",
"f#"
],
"languageName": "F#",
"name": "fsharp"
},
{
"aliases": [],
"languageName": "HTML",
"name": "html"
},
{
"aliases": [
"js"
],
"languageName": "JavaScript",
"name": "javascript"
},
{
"aliases": [],
"languageName": "KQL",
"name": "kql"
},
{
"aliases": [],
"languageName": "Mermaid",
"name": "mermaid"
},
{
"aliases": [
"powershell"
],
"languageName": "PowerShell",
"name": "pwsh"
},
{
"aliases": [],
"languageName": "SQL",
"name": "sql"
},
{
"aliases": [],
"name": "value"
},
{
"aliases": [
"frontend"
],
"name": "vscode"
},
{
"aliases": [],
"name": "webview"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}