【c++/C#】 混编的多线程,thread_local,

news/2024/10/19 11:05:45/

C++ 通过 thread_local 变量来实现线程局部存储(Thread-Local Storage, TLS),而 C++ 动态库并不需要直接感知线程是从 C++ 还是 C# 端创建的。线程的管理、调度等由操作系统处理,而 thread_local 的工作机制是与具体的编程语言无关的。让我们更深入地解释这个过程。

操作系统如何处理线程与 thread_local

  1. 线程管理由操作系统负责: 无论是从 C# 端还是从 C++ 端创建的线程,底层都是由操作系统来负责管理的。当 C# 创建一个线程,操作系统会为该线程分配独立的线程栈、寄存器和线程局部存储区(TLS)。这些操作系统级别的线程信息是语言无关的。

  2. thread_local 变量基于操作系统的线程局部存储 (TLS)

    • thread_local 变量在 C++ 中是依赖操作系统提供的线程局部存储(TLS)机制实现的。
    • 每个线程都有自己的 TLS 区域,当一个线程访问某个 thread_local 变量时,操作系统会根据当前线程的 TLS 信息返回该线程特有的变量副本。这一切是由操作系统底层机制来保证的,不需要编程语言显式感知线程的创建。

    因此,无论线程是由 C# 还是 C++ 代码创建,只要这些线程在操作系统中被标识为独立的线程,它们都会拥有独立的 TLS。C++ 中的 thread_local 变量在每个线程的 TLS 中都有一个独立的实例。

  3. 跨语言调用: 当你从 C# 中创建线程并通过 P/Invoke 调用 C++ 函数时,操作系统已经为该线程分配了独立的 TLS 区域。因此,当 C++ 函数访问 thread_local 变量时,它会使用当前线程的 TLS 区域中对应的变量副本。由于每个线程的 TLS 是独立的,这就确保了 thread_local 变量的线程安全。

C# 调用 C++ 动态库时的过程

  1. C# 线程的创建: 当你在 C# 中创建线程时(无论是通过 Thread 类还是 Task,等),底层的操作系统会为每个线程分配线程上下文,包括寄存器、栈空间以及线程局部存储(TLS)。这些信息由操作系统管理,而不需要 C++ 代码知道线程是从哪里创建的。

  2. P/Invoke 调用 C++ 函数: 当 C# 线程通过 P/Invoke 调用 C++ 动态库中的函数时,操作系统负责将该调用“附加”到正确的线程上。C++ 动态库中的 thread_local 变量会根据当前的线程信息在相应的 TLS 区域访问到当前线程的 thread_local 变量副本。

    • 这意味着 C++ 代码中的 thread_local 变量是线程局部的,不管调用是从 C++ 自身的线程还是从 C# 创建的线程进入的,操作系统都会自动处理这个线程上下文和变量访问。

具体示例分析

  1. C# 创建线程: C# 代码中创建两个线程,并调用 C++ 动态库中的函数:

    using System;
    using System.Threading;
    using System.Runtime.InteropServices;class Program
    {[DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]public static extern void SetLastError(string error);[DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]public static extern IntPtr GetLastError();static void SetAndPrintError(string error){SetLastError(error);IntPtr errorPtr = GetLastError();string lastError = Marshal.PtrToStringAnsi(errorPtr);Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: Last Error: {lastError}");}static void Main(){Thread t1 = new Thread(() => SetAndPrintError("Error from Thread 1"));Thread t2 = new Thread(() => SetAndPrintError("Error from Thread 2"));t1.Start();t2.Start();t1.Join();t2.Join();}
    }
    
  2. C++ 动态库的 thread_local 变量处理

#include <iostream>
#include <string>// 定义线程局部变量
thread_local std::string lastError = "";extern "C" __declspec(dllexport) void SetLastError(const char* error) {lastError = error;  // 每个线程拥有自己独立的lastError副本
}extern "C" __declspec(dllexport) const char* GetLastError() {return lastError.c_str();  // 返回当前线程的lastError副本
}

在这个示例中:

  • C# 中的 t1t2 分别代表两个不同的线程。
  • 每个线程通过 P/Invoke 调用 C++ 动态库的 SetLastErrorGetLastError 函数。
  • 因为每个线程都有自己独立的 TLS,当线程 1 访问 lastError 时,它看到的是自己线程的副本。同样,线程 2 也访问它自己的 lastError 副本,两个线程的变量互不干扰。

总结:

  1. C++ 不需要知道线程是从 C# 还是 C++ 创建的:操作系统负责所有线程的上下文,包括线程局部存储(TLS)。当 C++ 使用 thread_local 变量时,操作系统确保每个线程都有独立的副本,语言本身并不需要关心线程的来源。

  2. thread_local 在所有线程中独立工作:无论是 C# 创建的线程还是 C++ 自己创建的线程,每个线程在操作系统层面上都会有自己的 TLS 空间,thread_local 变量在每个线程的 TLS 空间中存储独立的副本。

  3. 线程安全性:由于每个线程都有独立的 thread_local 变量副本,多个线程并发调用时不会互相影响,因此是线程安全的。


http://www.ppmy.cn/news/1540237.html

相关文章

Flink时间窗口程序骨架结构

前言 Flink 作业的基本骨架结构包含三部分&#xff1a;创建执行环境、定义数据处理逻辑、提交并执行Flink作业。 日常大部分 Flink 作业是基于时间窗口计算模型的&#xff0c;同样的&#xff0c;开发一个Flink时间窗口作业也有一套基本的骨架结构&#xff0c;了解这套结构有助…

LLM 相关 API Key 配置速查

文章目录 LangChain智谱DashScope - QwenMoonshot AI - KimiOpenAItavily API Key 是个很简单的功能&#xff0c;但网站一大&#xff0c;就忘记在哪里&#xff0c;这里记录下 环境变量 及 Python 中常用设置代码为&#xff1a; import getpass import os# 简单配置 OPENAI_API…

2024年科技赋能教育,AI辅导引领新趋势

在近日举行的2024年中国国际服务贸易交易会上&#xff0c;教育服务展区凭借科技与教育深度融合的特色&#xff0c;成功吸引了大量参观者的注意。特别是主打AI个性化辅导的学习产品&#xff0c;更是成为了众多家长眼中的“省妈神器”。 “大语言模型”技术的快速发展及其科学化…

贪吃蛇游戏(代码篇)

我们并不是为了满足别人的期待而活着。 前言 这是我自己做的第五个小项目---贪吃蛇游戏&#xff08;代码篇&#xff09;。后期我会继续制作其他小项目并开源至博客上。 上一小项目是贪吃蛇游戏&#xff08;必备知识篇&#xff09;&#xff0c;没看过的同学可以去看看&#xf…

ShuffleNet通道混合轻量级网络的深入介绍和实战

ShuffleNet是一种轻量级的深度学习模型&#xff0c;它在保持MobileNet的Depthwise Separable Convolution&#xff08;深度可分离卷积&#xff09;的基础上&#xff0c;引入了通道混合&#xff08;Channel Shuffle&#xff09;机制&#xff0c;以进一步提升模型的性能和效率。 …

基于微信小程序的购物系统【附源码、文档】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

JS | JS中类的 prototype 属性和__proto__属性

大多数浏览器的 ES5 实现之中&#xff0c;每一个对象都有__proto__属性&#xff0c;指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖&#xff0c;同时有prototype属性和__proto__属性&#xff0c;因此同时存在两条继承链。 构造函数的子类有prototype属性。‌ …

反欺诈与数字信任:保障数字经济安全的关键

随着互联网和数字化技术的快速发展&#xff0c;全球数字经济规模迅速扩张&#xff0c;但同时也面临着前所未有的安全威胁。其中&#xff0c;欺诈行为在数字环境中的泛滥成为了主要的安全挑战之一&#xff0c;反欺诈与数字信任技术因此成为了保障在线交易、数字身份和数据安全的…