确保真正的线程安全——微软为什么不提供线程安全库

Microsoft Agent Framework 1.0:.NET原生AI智能体开发实战 AI智能体(Agent)是当前大模型应用的核心范式,指具备目标导向、上下文感知与工具调用能力的自主行为单元。其底层原理依赖于标准化的聊天接口(如IChatClient)、结构化提示工程(Instructions)与可序列化函数(AIFunction)机制,技术价值在于将非确定性LLM调用转化为可测试、可配置、可编排的.NET工程组件。典型应用场景包括客服工单分派、多角色协同分析、金融报告生成等需强逻辑控制与合规审计的业务系统。本文聚焦Microsoft Agent Framework 1.0这一微软官方推出 阅读详情
线程安全在高并发情况下是一个非常严重的问题。以下代码在多线程访问时,会出现问题。我们以List.Add为例,来说明在多线程访问下的状况。以下代码是List.Add的实现。
 
public   void  Add(T item) {      if  ( this ._size  ==   this ._items.Length)  this .EnsureCapacity( this ._size  +   1 );      this ._items[ this ._size ++ ]  =  item;      this ._version ++ ; }

当两个线程同时访问一个List的Add方法时,这个方法的第一条指令就可能出现不一致性了。因为,此时两个线程访问时_size都是一样的,正确情况下List应该执行EnsureCapacity(this._size + 2)而不再是EnsureCapacity(this._size + 1)了。为了确保List的线程安全,我们必须保证在任意时刻只能有一个线程来更改List的数据。这样我们的SimpleThreadSafeList就诞生了,在这个List中我们对其的每一个读写操作都加上一个排它锁。

public   class  SimpleThreadSafeList < T >  : IList < T >
{
    
private  List < T >  _data;
    
private   object  _syncRoot  =   new   object ();
    
public  SimpleThreadSafeList()
    {
        _data 
=   new  List < T > ();
    }
    
public   void  RemoveAt( int  index)
    {
        
lock  (_syncRoot)
        {
            _data.RemoveAt(index);
        }
    }
    
public   void  Add(T item)
    {
        
lock  (_syncRoot)
        {
            _data.Add(item);
        }
    }
    
//  others......
}

这样我们确保List的数据在任意时刻都只有一个线程对其进行访问,看起来安全很多了。不过,如果所谓线程安全是这么简单的话,微软为什么不提供一个ThreadSafeList呢?JaredPar MSFT在这篇文章中做了一个描述《Why are thread safe collections so hard?http://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx》,他解释微软不实现线程安全集合类,是因为以上这样的所谓“线程安全”并不是真正安全,我们可以从以下的代码中看出端倪。

var list  =   new  SimpleThreadSafeList < int > ();
//  ohters …
if (list.Count  >   0 )
{
    
return  list[ 0 ];
}

在以上代码中,我们创建了SimpleThreadSafeList这个类,其中有一个代码段就是用于获取list的默认值。如果有多个线程访问这段代码时,依然会出现数据不一致问题。即在执行“return list[0]”这条语句是,它是以“list.Count > 0”为前提的,当两个线程同时对这个SimpleThreadSafeList操作时,当前线程访问到list.Count大于0,但之后可能另一个线程将list清空了,这时候当前线程再来返回list[0]时就会出现IndexOutofRangeException了。SimpleThreadSafeList保证了List内部数据只能由一个线程来操作,但是对于上面的代码,它是无法保证数据不一致的。SimpleThreadSafeList仅能够被称为“数据线程安全”,这也是微软不提供线程安全集合类的原因了。JaredPar MSFT提出了一个真正解决线程安全的方法。那就是将SimpleThreadSafeList的_syncRoot 暴露出来。

public   class  SimpleThreadSafeList < T >  : IList < T >
{
    
private   object  _syncRoot  =   new   object ();
    
public   object  SyncRoot
    {
        
get
        {
            
return  _syncRoot;
        }
    }
    
//  others……
}

使用List时,需要使用到SyncRoot来加锁。 

lock (list.SyncRoot)
{
    
if (list.Count  >   0 )
    {
        
return  list[ 0 ];
    }
}

不过,使用这种方式,有几个缺陷。第一,没有一个良好的Guide来指导编写线程安全的代码;第二,当SyncRoot使用范围过大时,非常容易造成死锁。下面是一段可能产生死锁的代码。 

var list1  =   new  SimpleThreadSafeList < int > ();
var list2 
=   new  SimpleThreadSafeList < int > ();

new  Thread(() =>  
    {
       
lock (list1.SyncRoot)
       {
           list2.Add(
10 );  // 间接请求了list2.SyncRoot
       } 
    }).Start();

new  Thread(() =>  
    {
       
lock (list2.SyncRoot)
       {
           list1.Add(
10 );  // 间接请求了list1.SyncRoot
       } 
    }).Start();

对于死锁这个问题,我们采取的方法是使用Monitor.TryEnter,从而来避免一直死锁。对于前一个问题,我这边仅是基于DisposableLocker来实现尽可能的线程安全,对于如何使用,我目前依然没有一个良好的理论,只能说我们在设计高并发的API时,对多个线程可以同时访问的对象都需要加以判断从而来确定需要采用什么样的方式处理。

namespace  UIShell.OSGi.Collection
{
    
///   <summary>
    
///  Use Monitor.TryEnter to acquire a lock. This would not cause the dead lock.
    
///   </summary>
     public   class  DisposableLocker : IDisposable
    {
        
private   object  _syncRoot;
        
private   bool  _lockAcquired;
        
private   int  _millisecondsTimeout;
        
public  DisposableLocker( object  syncRoot,  int  millisecondsTimeout)
        {
            _syncRoot 
=  syncRoot;
            _millisecondsTimeout 
=  millisecondsTimeout;
            _lockAcquired 
=  Monitor.TryEnter(_syncRoot, _millisecondsTimeout);
            LogWhenAcquireLockFailed();
        }
        
private   void  LogWhenAcquireLockFailed()
        {
            
if  ( ! _lockAcquired)   // 这时候可能要如下处理:(1)记录日志;(2)记录日志并抛出异常;(3)记录日志,然后Block,重现死锁。
            {
                FileLogUtility.Error(
string .Format(
                    
" Accquire the lock timeout. The limited time is {0} milliseconds. " , 
                    _millisecondsTimeout
                    ));
            }
        }
        
public   void  Dispose()
        {
            Dispose(
true );
            GC.SuppressFinalize(
this );
        }
        
private   void  Dispose( bool  disposing)
        {
            
if  (disposing)
            {
                
if  (_lockAcquired)
                {
                    Monitor.Exit(_syncRoot);
                }
            }
        }
        
~ DisposableLocker()
        {
            Dispose(
false );
        }
    }
}

当出现死锁时,这里采用的解决方案是记录日志,然后继续运行。不过,这种方法有一定缺陷,可能在极端情况下引起数据不一致。因此,我们可能需要抛出异常或者让锁一直持续下去。SimpleThreadSafeList此时将以以下方式来实现。

public   class  SimpleThreadSafeList < T >  : IList < T >
{
    private   object  _syncRoot  =   new   object ();
    public   object  SyncRoot
    {
        get
        {
            return  _syncRoot;
        }
    }
    public   int  MillisecondsTimeoutOnLock {  get ;  private   set ; }
    public  DisposableLocker CreateLocker()
    {
        return   new  DisposableLocker(MillisecondsTimeoutOnLock, SyncRoot);
    }
    public   void  Add(T item)
    {
        using (CreateLocker())
        {
            _data.Add(item);
        }
    }
    //  others……
}

接下来使用List.CreateLocker来建立一个全局锁

using (list.CreateLocker())
{
    if (list.Count  >   0 )
    {
        return  list[ 0 ];
    }
}

大家可以看一下是否还有更好的方式来保证线程绝对安全。

 

原文地址:http://www.cnblogs.com/baihmpgy/archive/2011/07/13/2105018.html

UI自动化 线程安全问题1 是一个在Windows平台上使用的COM(Component Object Model)初始化函数。COM是微软开发的一种用于跨应用程序边界进行对象交互的技术。函数用于初始化COM库,设置线程的并发模型(单线程、多线程、单元线程等)。这是在使用COM对象之前必须执行的操作,以确保COM库能够在当前线程中正确运行。在Python中,如果你需要调用Windows的COM组件(例如,操作Excel、Word等Office应用程序),你也需要初始化COM库。这是因为Python本身并不直接支持COM,但是通过像这样 阅读详情

相关推荐

微软开源项目 Detours 详细介绍与使用实例分享

本文详细介绍微软开源库Detours,并分享使用Detours实现函数hook的实例。

dvlinker的技术专栏 1万+

Java并发 | 常见线程安全容器

线程安全数据类型通常提供了一些同步机制来保证数据的一致性。这些机制可以包括锁、互斥量、原子操作、无锁算法等。会在多个线程同时访问数据时进行同步操作,以保证每个操作的原子性和正确性。

devops 2043

思澈sdk-新建lcd

co5300至此有3个同ID的驱动,在co5300xxxx.c的LCD_DRIVER_EXPORT(co5300, LCD_ID, &lcdc_int_cfg, &CO5300_drv, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT, 2);2 在customer\peripherals\Kconfig_lcd中添加如下配置(注意修改的地方有两个LCD_USING_TFT_CO5300_143)5 在该文件中的config LCD_DPI配置项下添加:(这里300为DPI)

gongyuan073的专栏 1386

Linux线程篇(下)

有了之前对线程的初步了解我们学习了什么是线程,线程的原理及其控制。这篇文章将继续讲解关于线程的内容以及重要的知识点。

m0_69005269的博客 1215

不只是安装:用OrbbecSDK_ROS2玩转深度相机点云、滤波与DDS调优

本文深入探讨了如何通过OrbbecSDK_ROS2实现深度相机的高级应用,包括点云处理、深度图优化和DDS调优。详细介绍了点云采集与可视化技巧、多种滤波算法的配置与组合策略,以及针对高带宽数据传输的DDS高级调优方法,帮助开发者在机器人导航和三维重建等场景中提升性能。

weixin_29246195的博客 777

论文阅读笔记---Deep HDR Video from Sequences with Alternating Exposures

Kalantari N K, Ramamoorthi R. Deep HDR Video from Sequences with Alternating Exposures[C]//Computer Graphics Forum. 2019, 38(2): 193-205. 摘要: 在本文中,提出了一种基于学习的方法来,我们使用两个顺序卷积神经网络(CNN)对整个HDR视频重建过程进...

qq_25283239的博客 1447

C++11写的线程安全STL库

用C++写的程序,如果用到了自带的标准模板库STL,在多线程访问的时候如果不加锁很容易造成segment fault导致程序崩溃coredump,这个问题是C++语言开发挥之不去的meng'yan

踏莎行的博客 2490

洪水风险图制作全流程:HEC-RAS 与 ArcGIS 的耦合应用

本文系统介绍HEC-RAS水动力模拟软件在水利工程中的应用技术。内容涵盖:1)软件理论与安装;2)一维地形数据导入与河网构建;3)恒定流/非恒定流计算及模型调试要点;4)构筑物建模技术(桥梁、堰闸等);5)二维水动力模拟与溃坝分析;6)洪水风险图绘制。重点讲解水面线调试、糙率选取、报错分析等实操难点,通过案例教学实现从基础到工程应用的全流程技术掌握。该培训适用于防洪评价、河道治理等水利工程数值模拟需求。

2403_89974123的博客 1050

几种Java常用序列化框架的选型与对比

一 背景介绍序列化与反序列化是我们日常数据持久化和网络传输中经常使用的技术,但是目前各种序列化框架让人眼花缭乱,不清楚什么场景到底采用哪种序列化框架。本文会将业界开源的序列化框架进行对比测试,分别从通用性、易用性、可扩展性、性能和数据类型与Java语法支持五方面给出对比测试。通用性:通用性是指序列化框架是否支持跨语言、跨平台。易用性:易用性是指序列化框架是否便于使用、调试,会影响开发效率。可扩展...

十指波课堂的博客 791

CARLA 0.9.15从零开始:环境搭建与基础场景实战指南

本文是CARLA 0.9.15从零开始的完整环境搭建与基础运行教程。详细介绍了在Linux系统下配置Python环境、下载安装CARLA核心软件包、启动服务器以及运行第一个基础场景的步骤。重点演示了如何通过Python客户端脚本生成随机车流,并手动控制EGO车辆在仿真城市中行驶,为自动驾驶算法开发构建了坚实的仿真测试基础。

weixin_29053383的博客 777

C语言练习题:据说一个人的标准体重应该是其身高(单位:厘米)减去100、再乘以0.9所得到的公斤数。已知市斤的数值是公斤数值的两倍。现给定某人身高,请你计算其标准体重应该是多少?

题目内容: 据说一个人的标准体重应该是其身高(单位:厘米)减去100、再乘以0.9所得到的公斤数。已知市斤的数值是公斤数值的两倍。现给定某人身高,请你计算其标准体重应该是多少?(顺便也悄悄给自己算一下吧……) 输入格式: 输入第一行给出一个正整数H(100 < H ≤ 300),为某人身高。 输出格式: 在一行中输出对应的标准体重,单位为市斤,保留小数点后1位。 输入样例: 169 输出样例: 124.2 代码如下: #include<stdio.h> int main() { i

weixin_44877226的博客 2万+

探索Detours:微软开源的动态代码拦截库

是微软开发的一个开源库,它允许开发者轻松地修改其他进程中的函数调用行为,而无需重新编译或修改原始代码。这使得 Detours 成为了调试、监控和系统研究的强大工具。 ## 项目简介 Detours 使用钩子(hook)技术,可以在运行时“绕道”目标函数,将控制流转向自定义的处理函数,然后在适当的时候再恢复到原函数的执行。这种功能使得开发人员可以方便地对已有的应用程序进行扩展,例如添加日志记录、...

gitblog_00063的博客 727

2D激光slam四种算法建图效果对比

2d激光slam四种算法建图比较

m0_73791170的博客 8448

c#集合类的线程安全(整理)

QueueT> 类 MSDN的说法 线程安全 此类型的公共静态(在 Visual Basic 中为 Shared)成员是线程安全的。但不能保证任何实例成员是线程安全的。 只要不修改该集合,QueueT> 就可以同时支持多个阅读器。即便如此,从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。 若要确保枚举过程中的线程安全,可以在整个枚举过程中锁定

蓝叶菱的专栏 4万+

【虚幻引擎UE】UE5 阴影异常与优化

【虚幻引擎UE】UE5 阴影异常与优化解决方案合集

灵境引路人 3万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

php连接微软MSSQL完全攻略

在研究ezSQL的时候就看到了mssql_connect()等一些php提供的连接MSSQL的函数,本以为php这个开源的风靡世界的编程语言对连接微软的数据应该是不在话下的,但是到真正执行的时候,才发现困难多多。 一开始我下载的php版本是5.93的,下载下来添加环境变量等等搞了半天后,phpinfo()这个函数终于成功的运行在浏览器中了。然后当我在满世界的找php_mssql.dll,才发

吵吵博客 617

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

深入浅出GUI线程安全(一)

原文链接 http://twaver.servasoft.com/?p=531 作为一名TWaver Evangelist,我的工作目的就是通过与客户的交流、培训甚至现场支持等方式帮助用户将TWaver更好地应用到客户项目中,TWaver是这么一款横跨Java、Web、Flex和.NET等多种技术平台的GUI图形组件,因此我的工作内容简单的说就是:帮助客户正确使用GUI。 提到GUI线...

subnetwork的专栏 334

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

微软计划使用 Rust 取代 C和C+

新智元推荐 来源:InfoQ 作者:王文婧 【新智元导读】微软近日提出,未来将使用 Rust 作为 C、C++ 以及其他编程语言的替代方案,以改善应用程序的安全性的计划。 近日,微软安全响应中心(MSRC)团队在官网更新文章,就近日提出的最新计划,即未来将使用 Rust 作为 C、C++ 以及其他编程语言的替代方案以改善应用程序的安全性的计划,从空间内存安全、时间内存安全、数据竞...

ctrigger的专栏 5674

[.Net] 什么是线程安全的并发集合

System.Collections.Concurrent 为此,在.NET Framework中提供了System.Collections.Concurrent新的命名空间可以访问用于解决线程安全问题,通过这个命名空间能访问以下为并发做好了准备的集合。1.BlockingCollection 与经典的阻塞队列数据结构类似,能够适用于多个任务添加和删除数据,提供阻塞和限界能力。2.Co...

weixin_38170561的博客 496

Microsoft.Extensions完全指南:10个必备库构建企业级.NET应用

Microsoft.Extensions是.NET生态系统中构建生产级应用程序的核心框架集合,提供了企业级开发所需的各类基础设施库。这些库简化了依赖注入、配置管理、日志记录等常见任务,让开发者能够专注于业务逻辑的实现。无论你是刚开始接触.NET的新手,还是正在构建复杂企业系统的资深开发者,掌握Microsoft.Extensions系列库都是提升开发效率和代码质量的关键。 ## 为什么选择Mic

gitblog_00042的博客 917

Microsoft.IO.RecyclableMemoryStream 项目常见问题解决方案

Microsoft.IO.RecyclableMemoryStream 是一个开源项目,旨在提供一个高性能的 .NET MemoryStream 替代方案。该项目的主要目标是优化内存使用,减少垃圾回收(GC)的频率和时间,从而提高应用程序的性能。该项目使用 C# 作为主要的编程语言,适用于 .NET 平台。 ## 新手使用注意事项及解决方案 ### 1. 内存泄漏问题 **问题描述**:新手...

gitblog_01050的博客 1456

php连接sql server教程,php连接微软MSSQL(sql server)攻略

在研究ezSQL的时候就看到了mssql_connect()等一些php提供的连接MSSQL的函数,本以为php这个开源的风靡世界的编程语言对连接微软的数据应该是不在话下的,但是到真正执行的时候,才发现困难多多在研究ezSQL的时候就看到了mssql_connect()等一些php提供的连接MSSQL的函数,本以为php这个开源的风靡世界的编程语言对连接微软的数据应该是不在话下的,但是到真正执行的...

weixin_39568926的博客 474
上一篇: 如何写健壮的代码
下一篇: 随机抽样一致性算法(RANSAC)
jackfirst86
博客等级 码龄18年 28粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值