C#多线程编程实例实战教程

C#多线程编程实践 本文列举几种常用的c#多线程编程实例,其中主要参考了周公  http://blog.youkuaiyun.com/zhoufoxcn/article/details/4402999的文章,特此感谢。 -. WinForm多线程编程 1. new Thread()     新开一个线程,执行一个方法,没有参数传递,ThreadStart: private void DoWork() { 阅读详情

问题的提出

所谓单个写入程序/多个阅读程序的线程同步问题,是指任意数量的线程访问共享资源时,写入程序(线程)需要修改共享资源,而阅读程序(线程)需要读取数据。在这个同步问题中,很容易得到下面二个要求:

1) 当一个线程正在写入数据时,其他线程不能写,也不能读。  
2) 当一个线程正在读入数据时,其他线程不能写,但能够读。

在数据库应用程序环境中经常遇到这样的问题。比如说,有n个最终用户,他们都要同时访问同一个数据库。其中有m个用户要将数据存入数据库,n-m个用户要读取数据库中的记录。

很显然,在这个环境中,我们不能让两个或两个以上的用户同时更新同一条记录,如果两个或两个以上的用户都试图同时修改同一记录,那么该记录中的信息就会被破坏。

我们也不让一个用户更新数据库记录的同时,让另一用户读取记录的内容。因为读取的记录很有可能同时包含了更新和没有更新的信息,也就是说这条记录是无效的记录。

实现分析
  
规定任一线程要对资源进行写或读操作前必须申请锁。根据操作的不同,分为阅读锁和写入锁,操作完成之后应释放相应的锁。将单个写入程序/多个阅读程序的要求改变一下,可以得到如下的形式:

一个线程申请阅读锁的成功条件是:当前没有活动的写入线程。  
一个线程申请写入锁的成功条件是:当前没有任何活动(对锁而言)的线程。

因此,为了标志是否有活动的线程,以及是写入还是阅读线程,引入一个变量m_nActive,如果m_nActive > 0,则表示当前活动阅读线程的数目,如果m_nActive=0,则表示没有任何活动线程,m_nActive <0,表示当前有写入线程在活动,注意m_nActive<0,时只能取-1的值,因为只允许有一个写入线程活动。

为了判断当前活动线程拥有的锁的类型,我们采用了线程局部存储技术(请参阅其它参考书籍),将线程与特殊标志位关联起来。

申请阅读锁的函数原型为:public void AcquireReaderLock( int millisecondsTimeout ),其中的参数为线程等待调度的时间。函数定义如下:

public void AcquireReaderLock( int millisecondsTimeout )

{

// m_mutext很快可以得到,以便进入临界区

m_mutex.WaitOne( );   
// 是否有写入线程存在   
bool bExistingWriter = ( m_nActive < 0 );

if( bExistingWriter )

{ //等待阅读线程数目加1,当有锁释放时,根据此数目来调度线程

m_nWaitingReaders++;   
}  else

{ //当前活动线程加1
m_nActive++;   
}

m_mutex.ReleaseMutex();

//存储锁标志为Reader   
System.LocalDataStoreSlot slot = Thread.GetNamedDataSlot(m_strThreadSlotName);


object obj = Thread.GetData( slot );
LockFlags flag = LockFlags.None;

if( obj != null )   
flag = (LockFlags)obj ;   
if( flag == LockFlags.None )

{  
Thread.SetData( slot, LockFlags.Reader );
}   
else   
{
Thread.SetData( slot, (LockFlags)((int)flag | (int)LockFlags.Reader ) );
}
if( bExistingWriter )

{ //等待指定的时间   
this.m_aeReaders.WaitOne( millisecondsTimeout, true );

}      }

它首先进入临界区(用以在多线程环境下保证活动线程数目的操作的正确性)判断当前活动线程的数目,如果有写线程(m_nActive<0)存在,则等待指定的时间并且等待的阅读线程数目加1。如果当前活动线程是读线程(m_nActive>=0),则可以让读线程继续运行。

申请写入锁的函数原型为:public void AcquireWriterLock( int millisecondsTimeout ),其中的参数为等待调度的时间。函数定义如下:

public void AcquireWriterLock( int millisecondsTimeout )

{   
// m_mutext很快可以得到,以便进入临界区   
m_mutex.WaitOne( );
// 是否有活动线程存在

bool bNoActive = m_nActive == 0;

if( !bNoActive )

{  m_nWaitingWriters++;

}   else
{
m_nActive--;
}
m_mutex.ReleaseMutex();

//存储线程锁标志

System.LocalDataStoreSlot slot = Thread.GetNamedDataSlot( "myReaderWriterLockDataSlot" );

object obj = Thread.GetData( slot );   
LockFlags flag = LockFlags.None;  

if( obj != null )   
flag = (LockFlags)Thread.GetData( slot );   
if( flag == LockFlags.None )

{ Thread.SetData( slot, LockFlags.Writer );   
}  else

{  
Thread.SetData( slot, (LockFlags)((int)flag | (int)LockFlags.Writer ) );
}

//如果有活动线程,等待指定的时间

if( !bNoActive )
this.m_aeWriters.WaitOne( millisecondsTimeout, true );   
}

它首先进入临界区判断当前活动线程的数目,如果当前有活动线程存在,不管是写线程还是读线程(m_nActive),线程将等待指定的时间并且等待的写入线程数目加1,否则线程拥有写的权限。
释放阅读锁的函数原型为:public void ReleaseReaderLock()。函数定义如下:

public void ReleaseReaderLock()

{   
System.LocalDataStoreSlot slot = Thread.GetNamedDataSlot(m_strThreadSlotName );
LockFlags flag = (LockFlags)Thread.GetData( slot );
if( flag == LockFlags.None )

{   return;
}

bool bReader = true;  switch( flag )
{   
case LockFlags.None:   
break;
case LockFlags.Writer:
bReader = false;
break;
}

if( !bReader )
return;
Thread.SetData( slot, LockFlags.None );
m_mutex.WaitOne();
AutoResetEvent autoresetevent = null;
this.m_nActive --;
if( this.m_nActive == 0 )
{    if( this.m_nWaitingReaders > 0 )

{   
m_nActive ++ ;   
m_nWaitingReaders --;   
autoresetevent = this.m_aeReaders;
}   
else if( this.m_nWaitingWriters > 0)   
{  
m_nWaitingWriters--;

m_nActive --;

autoresetevent = this.m_aeWriters ;

}    }

m_mutex.ReleaseMutex();   
if( autoresetevent != null )   
autoresetevent.Set();   
}

释放阅读锁时,首先判断当前线程是否拥有阅读锁(通过线程局部存储的标志),然后判断是否有等待的阅读线程,如果有,先将当前活动线程加1,等待阅读线程数目减1,然后置事件为有信号。如果没有等待的阅读线程,判断是否有等待的写入线程,如果有则活动线程数目减1,等待的写入线程数目减1。释放写入锁与释放阅读锁的过程基本一致,可以参看源代码。

注意在程序中,释放锁时,只会唤醒一个阅读程序,这是因为使用AutoResetEvent的原历,读者可自行将其改成ManualResetEvent,同时唤醒多个阅读程序,此时应令m_nActive等于整个等待的阅读线程数目。


测试

测试程序取自.Net FrameSDK中的一个例子,只是稍做修改。测试程序如下,

using System;   
using System.Threading;  
using MyThreading;   
class Resource {   
myReaderWriterLock rwl = new myReaderWriterLock();   
public void Read(Int32 threadNum) {   
rwl.AcquireReaderLock(Timeout.Infinite);

try {   Console.WriteLine("Start Resource reading (Thread={0})", threadNum);

Thread.Sleep(250);

Console.WriteLine("Stop Resource reading (Thread={0})", threadNum);
}

finally {  rwl.ReleaseReaderLock();

}   }

public void Write(Int32 threadNum) {   
rwl.AcquireWriterLock(Timeout.Infinite);   
try {   
Console.WriteLine("Start Resource writing (Thread={0})", threadNum);
Thread.Sleep(750);
Console.WriteLine("Stop Resource writing (Thread={0})", threadNum);
}

finally {  rwl.ReleaseWriterLock();   
}   }   
}   
class App {

static Int32 numAsyncOps = 20;   
static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);   
static Resource res = new Resource();      
public static void Main() {   
for (Int32 threadNum = 0; threadNum < 20; threadNum++) {   
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);   
}

asyncOpsAreDone.WaitOne();
Console.WriteLine("All operations have completed.");   
Console.ReadLine();   
}

// The callback method's signature MUST match that of a System.Threading.TimerCallback   
// delegate (it takes an Object parameter and returns void)  
static void UpdateResource(Object state) {   
Int32 threadNum = (Int32) state;   
if ((threadNum % 2) != 0) res.Read(threadNum);   
else res.Write(threadNum);   
if (Interlocked.Decrement(ref numAsyncOps) == 0)   
asyncOpsAreDone.Set();   
}   
}

 

深度探索 C# 实战:多线程与并发编程实战技巧 在 C# 中实现多线程与并发编程时,重要的是选择合适的工具来应对不同的并发需求。对于简单的并行任务,ThreadPool和Task提供了简单高效的方式,而对于更复杂的同步问题,可以使用lock或Monitor来保护共享资源。异步编程 () 提供了一种无阻塞的方式来处理 I/O 密集型任务,而并发集合类提供了线程安全的容器,适合高并发场景。 阅读详情

相关推荐

C# 多线程编程实战:在代码的并行世界里狂飙

在 C# 中,多线程编程不仅让我们能够处理复杂的并发任务,还能极大提升程序的执行效率,使得代码在“并行世界”中如同飞驰在高速公路上一样,充满速度与力量。多线程编程是提升 C# 应用性能的利器,它使得我们能够在并行世界中自由驰骋,充分发挥多核处理器的能力,优化程序的响应速度和资源利用率。当需要处理大文件或大量数据时,使用多线程可以将任务分解成多个独立的线程处理,从而缩短文件处理的时间。:在需要处理大量数据或长时间运行任务的应用中,多线程可以实现任务的异步处理,减少用户等待的时间。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1373

C#多线程编程实例实战

本文通过对常见的单个写入/多个阅读程序的分析来探索c#的多线程编程。

C#异步编程与多线程操作实战

异步编程:主要用于 I/O 密集型任务,能够让应用在等待外部资源时继续执行其他操作,适用于网络请求、文件操作等。多线程编程:适用于 CPU 密集型任务,能够通过并行执行多个计算密集型任务来提高效率,但也会有较大的性能开销,尤其是在线程过多的情况下。在实际应用中,你可能会同时使用异步编程和多线程编程。比如,在 UI 应用中,异步编程可以防止界面卡死,而后台计算任务可以使用多线程并行执行。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 947

C#多线程实例汇总

我汇总了一下关于C#多线程的使用方法,包括一些简单的锁和线程池的常用方法,虽然古老了一些,但是在一些老的项目中还是能经常用到的,多线程和异步有点儿小区别,分两篇文章来汇总,下一篇我再总结一下关于C#异步的新旧使用方法。

Dahlin哥's 博客 4820

C#多线程编程实战教程与实例解析

在当今高性能、高并发的软件开发环境中,多线程编程已成为不可或缺的技能之一。C#作为一门面向现代应用开发的语言,提供了强大且灵活的多线程支持,使开发者能够充分利用多核CPU资源,提升程序的响应速度与处理能力。本章将从基础概念入手,探讨线程与进程的本质区别,并阐述多线程在现代软件架构中的重要地位。我们将介绍C#中多线程编程的核心体系结构,包括Thread类、Task模型以及async/await异步编程范式。同时,也会简要说明多线程所带来的并发优势与潜在挑战,如线程同步、资源共享、死锁等问题。

weixin_42511832的博客 895

C#编程实例与实践:100个精选案例

创建自定义的LINQ扩展方法可以使查询更简洁,并且能够复用特定的查询逻辑。下面是一个示例,我们创建一个扩展方法WhereIf,它在条件为真时才应用Where过滤:else// 使用自定义扩展方法在此代码中,WhereIf方法会根据条件参数的真假,决定是否应用Where过滤。这使得在构建复杂的查询时可以更加灵活地控制查询逻辑。通过本章的介绍,我们了解了LINQ的强大功能以及在不同数据源上进行数据查询的方式。

weixin_36464343的博客 1210

C# 多线程(菜鸟教程及爱整理)

C# 多线程--菜鸟教程 线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。 线程生命周期 线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。 下面列出了线程生命周期中的各种状态: 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。 不可运行状态:

weixin_45582785的博客 1万+

C#编程实战案例大全1200例

本文还有配套的精品资源,点击获取 简介:《C#开发实战1200例》是一本全面的C#编程教程,包含1200个实例,覆盖C#编程的核心概念和技术。本书分为多个章节,每个章节包含若干个案例,覆盖从基础语法到高级特性的广泛内容。书中的实例包括面向对象编程、异常处理、文件操作、多线程、数据库编程、GUI设计和网络编程等方面,使读者能够通过实战提升编程技能。 1. C#基础语法 ...

weixin_35835018的博客 482

C#多线程实例

private void thread_Click(object sender, EventArgs e) { long sum; Console.WriteLine("开始时间:"+ DateTime.Now); MyThread myThread1 = new MyThread(-500000000, 0)

content6的博客 1300

.NET C#编程基础与实例实战教程

本文还有配套的精品资源,点击获取 简介:本教程旨在为初学者提供深入的C#基础知识和.NET技术的实践指导。涵盖C#基础语法、面向对象编程、异常处理、文件操作、Windows Forms、*** Web开发、LINQ查询、多线程以及.NET Framework类库的应用。通过实例教学和需求分析文档,帮助学习者掌握C#编程技能,并为未来的进阶学习打下坚实基础。 1. C#...

weixin_36282234的博客 1751

C#.NET多线程编程全方位实例教程201902

htmltable {th, td {th {pre {简介:本教程旨在通过实例深入解析.NET框架下C#语言的多线程编程,涵盖了从基础使用到互斥的高级概念。介绍了线程创建、同步机制、线程优先级、生命周期,以及线程互斥的策略,包括Monitor和Mutex的使用。此外,还包括了ThreadPool、Task并行库TPL和async/await的高级技术。六个实例演示了如何在实际场景中应用这些技术,帮助开发者理解并掌握多线程编程,提高程序性能。

weixin_28713083的博客 1048

C#多线程编程实例 线程与窗体交互

C#多线程编程实例 线程与窗体交互代码: public partial class Form1 : Form { //声明线程数组 Thread[] workThreads = new Thread[10]; public Form1() { InitializeComponent();

无知人生,记录点滴 3万+

C#.NET多线程编程实战:6个实例深入理解

简介:C#在.NET框架中提供了强大的多线程编程能力,使得开发者能够利用多核处理器进行并行处理。本资源包含了六个涵盖多线程基础和高级特性的C#.NET实例,如线程创建、线程同步与互斥、线程池使用、线程同步原语、异步编程模型、线程通信、线程状态管理、异常处理、线程局部存储和线程优先级等。开发者通过运行和分析这些实例,可以深入学习和掌握C#.NET多线程编程的实战应用。

weixin_31776191的博客 1368

C#.NET多线程编程与实例教程

本文还有配套的精品资源,点击获取 简介:C#在.NET框架中提供了丰富的多线程支持,本教程将详细介绍如何创建和管理线程,包括线程的基本使用、线程同步与互斥,以及线程安全的数据访问和异步编程模式。具体实现方法包括使用 Thread 类、 Monitor 、 Mutex 、 Semaphore 等关键字和类,以及 ...

weixin_35706067的博客 1357

C#多线程编程实战指南

本文还有配套的精品资源,点击获取 简介:本教程以多线程编程在C#中的实际应用为核心,为初学者提供了一个理解并实践多线程的起点。介绍了C#中线程的基本概念、Thread类的使用、ThreadPool的线程池机制、Task Parallel Library (TPL)的高级并行编程能力以及async/await异步编程模式。同时,讲解了多线程编程中常见的同步问题和解决工具,例如...

weixin_34620658的博客 967

C#多线程编程入门到实践教程

本文还有配套的精品资源,点击获取 简介:本教程为C#多线程技术的初学者量身打造,介绍了多线程的基本概念、线程生命周期、同步与互斥、线程池的使用、异步编程模式、线程优先级设置、避免死锁与竞态条件,以及实现线程安全的数据结构等核心内容。通过实例代码,学习者将能够掌握如何高效利用多线程提升程序性能,适用于处理大量数据或复杂计算任务。 1. C#多线程基础知识介绍 ...

weixin_34620658的博客 948
上一篇: C#中ref和out的使用小结
下一篇: 什么是CTS, CLS和CLR
浩西
博客等级 码龄19年 47粉丝 48原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值