.Net基础:详解.NET编程过程中的线程冲突 (1)

68、.NET 中的序列化与多线程技术详解 本文深入探讨了 .NET 中的序列化与多线程技术。在序列化部分,对比了 CLR 序列化与 JSON.NET 的特点和适用场景,包括对象引用处理、循环引用支持及与其他系统的交互能力。多线程部分介绍了基础概念如处理器、核心和硬件线程,并详细演示了如何使用 Thread 类和线程池实现多线程编程,同时讨论了同步问题及其解决方案。通过合理选择序列化方式和多线程策略,可以有效提升应用程序的性能与可维护性。 阅读详情

 

一、什么是线程冲突

 

线程冲突其实就是指,两个或以上的线程同时对同一个共享资源进行操作而造成的问题。

 

一个比较经典的例子是,用一个全局变量做计数器,然后开N个线程去完成某个任务,每个线程完成一次任务就将计数器加一,直到完成100次任务。如果不考虑线程冲突问题,用类似下面的代码去做,则很可能会超额完成任务,线程越多,完成任务次数超出100次的可能性就越大。

 

伪代码如下:

 

int count = 0;//全局计数器

void ThreadMethod()//运行在每个线程的方法

{

while( true )

{

if ( count >= 100 )//如果达到任务指标

break;//中断线程执行

DoSomething();//完成某个任务

count++;

}

}

//省略线程的创建等代码。

 

具体的,为什么会超额完成任务的原因在这里我就不赘述了,这个例子在单线程环境中是绝对不会超额完成任务的。

 

当然,在这个例子中,将count++放到if语句中,也许能降低一些事故发生的概率,但那不是绝对的,换言之这样的程序不能杜绝超额完成任务的可能。

 

其实从线程冲突的定义中我们不难发现,要造成线程冲突有两个必要条件:多线程和共享资源。这两个条件中有一个不成立,就不可能发生线程冲突问题。

 

所以,在单线程环境中,是不存在线程冲突的问题的。不过很可惜的是,我们的软件早已进化到了多进程多线程的时代,单线程的程序几乎是不存在的,无论是WinForm还是WebForm,程序运行的环境都是多线程的,而不论你自己是不是明确的开启了一个线程。

 

既然多线程是不可避免的,那么要避免线程冲突就只能从共享资源来开刀了。

 

二、线程安全的资源

 

如果大家经常看MSDN或者VS帮助中的.NET类库参考的话,就不难发现几乎所有的类型都有这么一句话的描述:“此类型的任何公共 static(在 Visual Basic中为 Shared) 成员都是线程安全的。但不保证所有实例成员都是线程安全的。”那么线程安全到底是什么意思?

 

其实线程安全很简单,就是指一个函数(方法、属性、字段或者别的)在同一时间被不同线程使用,不会造成任何线程冲突的问题。就说这个东西是线程安全的。

 

接下来来谈谈什么样的资源是线程安全的。

 

之所以使用资源这个词,是因为线程冲突不仅仅会发生在共享的变量上,两个线程同时对同一个文件进行读写,两个程序同时用同一个端口与同一个地址进行通信,都会造成线程冲突。只不过是操作系统和帮我们协调了这些冲突而已。

 

一个线程安全的资源即是指,在不同线程中使用不会导致线程冲突问题的资源。

 

一个不能被改变的资源是线程安全的,比如说一个常量:

 

 

const decimal pai = 3.14159265;//C++: const double pai = 3.14159265;

 

 

因为pai的值不可能被改变,所以在不同的线程中使用也不会造成冲突。换言之它在不同的线程中同时被使用和在一个线程中被使用是没有区别的,所以这个东西是线程安全的。

 

同样的,在.NET中,一个字符串的实例也是线程安全的,因为字符串的实例在.NET中也是不可以被改变的。一个字符串的实例一旦被创建,对其所有的属性、方法调用的结果都是唯一确定的,永远不会改变的。所以.NET类库参考中String类型才有:“此类型是线程安全的。”,与之类似的Type类型、Assembly类型,都是线程安全的。

 

但string的实例是线程安全的,却不代表string的变量是线程安全的,换言之,假设有一个静态变量:

 

 

public static string str = “123”;

 

 

str不是线程安全的,因为str这个变量的字符串实例可以被任何线程修改。

 

再考虑这样的例子:

 

public static readonly SqlConnection connection = new SqlConnection( “connectionString” );

 

虽然connection本身虽然是线程安全的,但connection的任何成员都不是线程安全的。

 

比如说,我在一个线程中对这个connection调用了Open方法,然后进行查询操作。但在同一时刻,另一个线程调用了Close方法,这时候,就出现错误了。

 

但,单纯的使用connection而不使用其任何成员,比如说if ( connection != null )这样的代码,是不存在线程冲突的。

 

线程安全的资源其实还有很多,在此不一一赘述。

 

对于.NET Framework的类型的成员来说,只读的字段是线程安全的。

 

那么对于属性和方法来说,怎么知道是不是线程安全的?

 

三、线程安全的函数

 

因为属性和方法都是函数组成的,所以我们探讨一下什么是线程安全的函数。

 

上面我们说到,线程冲突的必要条件是多线程和共享资源。那么如果一个函数里面没有使用任何可能共享的资源,那么就不可能出现线程冲突,也就是线程安全的。比如说这样的函数:

 

 

public static int Add( int a, int b ){

return a + b;

}

 

 

这个函数中所使用的所有的资源都是自己的局部变量,而函数的局部变量是储存在堆栈上的,每个线程都有自己独立的堆栈,所以局部变量不可能跨线程共享。所以这样的函数显然是线程安全的。

 

但值得注意的是:下面的函数不是线程安全的:

 

 

public static void Swap( ref int a, ref int b )//C++: void Swap( in& a, int& b )

{

int c = a;

a = b;

b = c;

}

 

 

因为ref的存在,使得函数的参数是按引用传递进来的,换言之a和b看起来是函数的局部变量,但实际上却是函数外面的东西,如果这两个东西是另一个函数的局部变量,倒也没有问题,如果这两个东西是全局变量(静态成员),就不能确保没有线程冲突了。而在上个例子中,a和b在传入函数之时,就做了一个拷贝的动作,所以传进来的a、b到底是全局变量还是静态成员都没有关系了。

 

同样,这样的函数也不是线程安全的:

 

 

public static int Add( INumber a, INumber b )//C++: int Add( INumber* a, INumber* b );

{

return a.Number + b.Number;

//C++: return a->Number + b->Number;

}

 

 

原因在于a和b虽然是函数的内部变量没错,但a.Number和b.Number却不是,它们不存在于堆栈上,而是在托管堆上,可能被其他线程更改。

 

但只使用局部变量的函数在.NET类库中是很少的,但.NET类库中还是有那么多线程安全的函数,是为什么呢?

 

因为,即使一个函数使用了共享资源,如果其所使用的共享资源都是线程安全的,则这个函数也是线程安全的。

 

比如说这样的函数:

 

 

private const string connectionString = “…”;public string GetConnectionString()

{

return connectionString;

}

 

 

虽然这个函数使用了一个共享资源connectionString,但因为这个资源是线程安全的,所以这个函数还是线程安全的。

 

同样的,我们可以得出,如果一个函数只调用线程安全的函数,只使用线程安全的共享资源,那么这个函数也是线程安全的。

 

这里有一个容易被忽略的问题,运算符。并不是所有的运算符(尤其是重载后的运算符)都是线程安全的。

 

 

四、互斥锁

 

有时候我们不得不面对线程不安全的问题,比如说在一开始提出来的那个例子,多线程完成100次任务,我们怎样才能解决这个问题,一个简单的办法就是给共享资源加上互斥锁。在C#中这很简单。比如一开始的那个例子:

 

 

public static class Environment{public static int count = 0;//全局计数器

}

//…void ThreadMethod()//运行在每个线程的方法

{

while( true )

{

lock ( typeof( Environment ) )

{

if ( count >= 100 )//如果达到任务指标

break;//中断线程执行

DoSomething();//完成某个任务

count++;}}}

 

 

通过互斥锁,使得一个线程在使用count字段的时候,其他所有的线程都无法使用,而被阻塞等待。达到了避免线程冲突的效果。

 

当然,这样的锁会使得这个多线程程序退化成同时只有一个线程在跑,所以我们可以把count++提前,使得lock的范围缩小,如这样:

 

 

void ThreadMethod()//运行在每个线程的方法{

while( true )

{

lock ( typeof( Environment ) )

{

if ( count++ >= 100 )//如果达到任务指标

break;//中断线程执行

}

DoSomething();//完成某个任务

}}

 

 

最后来聊聊SyncRoot的问题。

 

用.NET的一定会有很多朋友困惑,为什么对一个容器加锁,需要这样写:

 

lock( Container.SyncRoot )

 

而不是直接lock( Container )

 

因为锁定一个容器并不能保证不会对这个容器进行修改,考虑这样一个容器:

 

 

public class Collection{

private ArrayList _list;

public Add( object item )

{

_list.Add( item );

}

public object this[ int index ]

{

get { return _list[index]; }set { _list[index] = value;}

}}

 

 

看起来,将其lock起来后,就万事大吉了,没有人能修改这个容器,但实际上这个容器不过是用一个ArrayList实例来实现的,如果某段代码绕过这个容器而直接操作_list的话,则对这个容器对象lock也不可能保证容器不被修改了。

掌握多线程编程与.NET程序集应用 反射是一种在运行时动态地检查或修改程序集的行为。它允许程序在执行期间获取类型信息、创建对象实例、调用方法、访问属性和字段等。反射是.NET框架中非常强大的一个功能,尤其在处理未知类型或动态加载组件时显得尤为重要。在.NET中,反射主要由命名空间下的类和接口提供支持。核心类包括AssemblyTypeMethodInfo等。Assembly类代表了一个程序集,可以从这里加载和查询类型信息。Type类提供了关于类型的信息,包括字段、方法、属性等。MethodInfo。 阅读详情

相关推荐

C# .NET中的LockFree编程实践

LockFree并发编程技术是实现高效并发控制的一种方法,它避免了传统锁机制带来的性能瓶颈和复杂性。通过这一技术,开发者可以创建能够抵御线程冲突而无需显式锁定和解锁的数据结构。JmBucknall.Structures库是一个支持.NET环境的开源库,它提供了多种无锁数据结构,用于在多线程环境中高效地处理并发访问。库的安装非常简单,可通过NuGet包管理器安装,命令为:安装完毕后,在代码中引入命名空间:初始化库中的无锁数据结构也非常直接,只需创建相应的实例即可:// 创建无锁队列实例。

weixin_36277197的博客 1427

线程安全问题——线程冲突、死锁

线程冲突、死锁等问题都属于线程安全问题,具体的:两个线程对一个变量进行操作,但是没有上锁,即没有进行同步操作,就像买车票的时候多个窗口一起卖,但是车票数没有减去。车票被重复卖出。就是不安全。还有,被多个线程操作的变量改为静态量加static,才安全。 在java中,对于多线程冲突问题,我们采用一种常规的解决方法,就是同步机制,即对操作加synchronized关键字。 synchronized 方法控制对类成员变量的访问: 每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实

GoodLinGL的博客 1763

xenomai内核解析--嵌入式实时linux概述

Linux系统作为一个GPOS(通用操作系统)发展至今已经非常成熟可靠了,并且由于遵循GPL协议,开放所有系统源代码,非常易于裁剪。更重要的是,与其他开源的GPOS或RTOS相比,Linux系统支持多种处理器、开发板,提供多种软件开发工具,同时Linux系统对网络和图形界面的支持非常出色。显然,选择Linux操作系统在产品的开发周期和成本控制方面都有巨大优势。 虽然Linux系统功能强大、实用性强...

wsg1100 3699

【多线程】| 线程冲突解决方案

同一进程内的线程是共享同一内存空间的,所以在多个线程的进程里,线程是可以同时操作这个进程空间的数据的,这样就容易造成线程冲突的情况。举个小李子:一个房子里(代表一个进程),只有一个厕所(代表一个资源)。屋子里面有两个人A和B(代表两个线程),共用这个厕所。一天A去上厕所了,一上就仨小时。到第三个小时的时候,B实在忍不住了,直接就开门进去把在里面玩手机的A轰了出来。然后A就被赶出来了,嘴里骂骂咧咧……这里说明了什么呢???

狮子也疯狂的博客 3370

SAP Workflow审批功能实例详细过程

SAP Workflow审批功能实例详细过程

陈送平的博客 6566

不用写触发器!帆软填报报表自动记录创建/修改时间的终极方案

本文详细介绍了帆软填报报表中无需编写数据库触发器,即可自动记录数据创建时间和修改时间的终极方案。该方案利用帆软内置函数和单元格条件属性,在应用层实现日期自动生成,有效规避了触发器带来的权限、维护和部署难题,为数据填报提供了更轻量、灵活且易于维护的解决方案。

weixin_28040125的博客 358

线程冲突详解

今天被朋友问到一个问题,某个函数在多线程环境中,会不会有冲突。在帮他解答这个问题的过程中,发现很多人对线程冲突和线程安全的理解并不是很全面,所以萌发了写这么一篇文章的想法,也算是对朋友的问题的一个完整的解答。 水平有限,如有错漏指出敬请指出。   一、什么是线程冲突 线程冲突其实就是指,两个或以上的线程同时对同一个共享资源进行操作而造成的问题。 一个比较经典的例子是,用一个全局变量做计数...

weixin_33928467的博客 404

TASK02【datawhale组队学习】视觉感知与手眼协调,计算机视觉在机器人中应用,手眼标定算法实现,深度估计与3D重建

参考教程AI硬件与机器人技术教程:https://github.com/datawhalechina/ai-hardware-robotics视觉感知与手眼协调,计算机视觉在机器人中应用,手眼标定算法实现,深度估计与3D重建单目深度估计 (Monocular Depth Estimation)DPT 的全称是 Dense Prediction Transformer。

m0_52024881的博客 1191

多线程冲突处理方法,锁

多线程冲突处理方法,锁

weixin_66455428的博客 1671

AVDepthData、LiDAR 深度数据与人像模式全解析:iOS 真实景深图采集与处理实战

在 iOS 高端影像系统中,真实景深信息的采集与利用已成为区分旗舰设备与基础设备的重要能力。Apple 自 iPhone 7 Plus 引入双摄虚化以来,逐步发展出基于 AVDepthData、TrueDepth 与 LiDAR 的多维深度感知技术,并开放接口供开发者获取高精度深度图,构建背景虚化、人像分割、3D 重建等多种功能。本文将深入解析 AVDepthData 的结构与使用方式,系统讲解 LiDAR 采集架构、人像模式的景深融合逻辑,以及如何在实际项目中采集、预览并处理深度图数据,实现具备工程可用性

努力分享一些人工智能、计算机视觉、影像等相关的知识干货! 1863

【unity小技巧】委托(Delegate)的基础使用和介绍

在Unity中,委托(Delegate)是一种用于处理方法回调的特殊类型。它允许您将方法作为参数传递给其他方法,并在需要时调用这些方法。委托在游戏开发中非常有用,特别是在事件处理、消息传递和异步操作方面。它提供了一种灵活的方式来注册和触发方法,而无需显式地知道这些方法的实现细节。个人理解:可以把委托理解为一个容器,容器里面放可以放很多函数方法。一调用委托,里面所有的函数方法都会同步被执行。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 7236

多线程冲突了怎么办?(多线程的同步与互斥问题)

在单核CPU系统⾥,为了实现多个程序同时运⾏的假象,操作系统通常以时间⽚调度的⽅式,让每个进程执⾏每次执⾏⼀个时间⽚,时间⽚⽤完了,就切换下⼀个进程运⾏,由于这个时间⽚的时间很短,于是就造成了「并发」的现象。另外,操作系统也为每个进程创建巨⼤、私有的虚拟内存的假象,这种地址空间的抽象让每个程序好像拥 有⾃⼰的内存,⽽实际上操作系统在背后秘密地让多个地址空间「复⽤」物理内存或者磁盘。如果⼀个程序只有⼀个执⾏流程,也代表它是单线程的。

m0_74229735的博客 1559

天气预测模型:基于时间序列分析的数学建模与实战案例

目录引言时间序列分析介绍使用Matlab进行天气预测实战案例:天气预测结论天气预测一直是气象学和相关领域的重要研究问题之一。数学建模是进行天气预测的有效方法之一,其中时间序列分析是常用的技术。在本篇博客中,我们将介绍时间序列分析的基本原理,演示如何使用Matlab进行模型建立和预测,并通过一个实战案例来展示模型的应用和分析。时间序列分析是一种用于分析和预测时间序列数据的统计方法。天气数据通常具有时间依赖性和随机性,因此时间序列分析可以帮助我们了解天气的变化趋势和规律,并提供对未来天气的预测。时间序列分析通常

YOLO 1545

DotNetGuide进程与线程:.NET中的并发模型

你是否在.NET开发中遇到过这些问题:界面卡顿无响应、数据处理效率低下、高并发场景下程序崩溃?根据Stack Overflow 2024年开发者调查,**73%的.NET开发者**认为并发编程是日常工作中最具挑战性的任务之一。本文将系统解析进程与线程的底层原理,全面对比.NET中的4种异步模式与4种多线程实现方式,提供12个实战案例和8个性能优化技巧,帮你彻底掌握并发编程精髓。 读完本文你将获得...

gitblog_00620的博客 1216

《低功耗方法学》翻译——卷首语

低功耗设计方法学。

记录所学,分享知识,结识挚友 991

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

.NET中的线程锁

常用的锁机制包括lock关键字、Monitor类、Mutex类以及Semaphore等。线程锁的使用可以有效避免数据竞争和不一致的状态,但也需要谨慎使用,避免死锁、性能问题等。欢迎关注我的公众号“Net分享”,技术文章第一时间推送,随缘更新 , 分享一些你可能注意不到的细节。

混迹自留地 1275

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

HALCON多线程编程中的静态变量陷阱:如何避免内存浪费和线程冲突

本文深入剖析了HALCON多线程编程中静态变量引发的内存浪费与线程冲突两大核心陷阱。通过解析HALCON的内存管理机制与多线程架构,文章提供了从避免静态变量、使用线程局部存储,到实施安全同步与资源池模式等实战解决方案,旨在帮助高级开发者构建高效、稳定的高性能机器视觉系统。

android的专栏 884

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

转:.NET中的线程

<br />此文比较详细的讲述了.NET中的线程,很好的学习资料,新手值得学习。<br /><br />目录<br /><br /><br /> <br />基础篇怎样创建一个线程受托管的线程与 Windows线程前台线程与后台线程名为BeginXXX和EndXXX的方法是做什么用的 异步和多线程有什么关联<br />WinForm多线程编程篇我的多线程WinForm程序老是抛出InvalidOperationException ,怎么解决?Invoke,BeginInvoke干什么用的,内部是怎么实现的每

tanhua103292的专栏 751

【.NET线程--进阶(一)】--线程方法详解

线程的优缺点决定了在开发过程中是否使用多线程,另外灵活运行单线程的方法来实现灵活的控制线程,两个Demo使用了线程的基本方法,能够更加深刻的了解它们的使用。线程和线程之间的调用关系,以及如何实现线程间的数据传递及检索。

航帆远洋 6726

Numpy.NET多线程编程必知:避免死锁与访问冲突的实战指南

在科学计算和机器学习应用中,多线程编程是提升性能的关键技术。然而,当你在C#/F#项目中使用Numpy.NET时,多线程编程需要特别注意Python的全局解释器锁(GIL)机制。本指南将为你揭示Numpy.NET多线程编程的核心要点,帮助你避免常见的死锁和访问冲突问题,确保应用程序的稳定运行。😊 ## 为什么Numpy.NET多线程编程如此重要? Numpy.NET是Python NumPy

gitblog_00753的博客 495

NET中的线程锁

在.NET中,线程锁(Thread Locking)是一种重要的同步机制,用于确保多个线程在访问共享资源时不会发生冲突,保证线程安全。线程锁的核心目标是避免数据竞争和不一致的状态,确保在同一时刻只有一个线程能够访问特定的资源或代码块。本文将详细讲解.NET中的线程锁,帮助你理解其工作原理、常见实现方式及注意事项。常见的线程锁有:• 自旋锁:当线程尝试获取锁时,它会重复执行一些简单的指令,直到锁可用...

dotNET跨平台 249

【转】1.1异步编程:线程概述及使用

从此图中我们会发现 .NET 与C# 的每个版本发布都是有一个“主题”。即:C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#4.0动态语言→C#5.0异步编程。现在我为最新版本的“异步编程”主题写系列分享,期待你的查看及点评。 传送门:异步编程系列目录…… 开始:《异步编程:线程概述及使用》 示例:异步编程:线程概述及使用.rar 做交互式客户端应用程序,用户总希望程序能时刻响应UI操作;做高性能服务器开发,使用者总希望服务器能...

sinolover的专栏 597

MonoGame多线程编程:避免GraphicsDevice访问冲突的5个技巧

MonoGame是一个强大的跨平台游戏开发框架,它基于Microsoft XNA架构,为开发者提供了丰富的图形渲染和游戏开发功能。在多线程环境下,GraphicsDevice的访问冲突是开发者经常遇到的挑战。本文将分享5个实用技巧,帮助你避免多线程编程中的GraphicsDevice访问冲突问题。 ## 🎯 理解MonoGame多线程架构 MonoGame内置了专门的线程管理机制,位于[Mo

gitblog_00226的博客 466

讲述.NET中的线程(转)

写在前面,原文:http://www.cnblogs.com/yizhu2000/archive/2008/01/03/1011958.html此文比较详细的讲述了.NET中的线程,很好的学习资料,新手值得学习。目录 基础篇 怎样创建一个线程受托管的线程与 Windows线程前台线程与后台线程名为BeginXXX和EndXXX的方法是做什么用的 异步和多线程有什么关联

275

在.NET中的线程处理(1)

 无论您是为具有单个处理器的计算机还是为具有多个处理器的计算机进行开发,您都希望应用程序为用户提供最好的响应性能,即使应用程序当前正在完成其他工作。要使应用程序能够快速响应用户操作,同时在用户事件之间或者甚至在用户事件期间利用处理器,最强大的方式之一是使用多个执行线程。线程与线程处理操作系统使用进程将它们正在执行的不同应用程序分开。线程是操作系统分配处理器时间的基本单元,并且该进程中可

公主笔记(快乐的蓝精灵) 673

异步编程:线程概述及使用

来自:http://www.cnblogs.com/heyuquan/archive/2012/12/16/2820775.html 异步编程:线程概述及使用 =============C#.Net 篇目录============== 从此图中我们会发现 .NET 与C# 的每个版本发布都是有一个“主题”。即:C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#4.0动态

码农 1193
上一篇: ASP.NET 安全认证—— 用Form 表单认证实现单点登录(Single Sign On)
下一篇: JS技巧:在IE浏览器中正确显示PNG透明图片
webclass
博客等级 码龄19年 14粉丝 90原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值