接昨天谈及的线程同步问题,今天介绍一个比较简单的类,Interlocked。它提供了以线程安全的方式递增、递减、交换和读取值的方法。

它的特点是:

1、相对于其他线程同步技术,速度会快很多。

2、只能用于简单的同步问题。

比叫好理解,不再赘述,给一个我们常用的单例模式的 Interlocked 实现:

        class SourceManager
{
private SourceManager() { } private static SourceManager sourceManager;
public static SourceManager Instance
{
get
{
if (sourceManager == null)
{
/* lock 实现方式
功能与以下 Interlocked.CompareExchange 相同 lock (this)
{
if (sourceManager == null)
{
sourceManager = new SourceManager();
}
} */
Interlocked.CompareExchange<SourceManager>(ref sourceManager, new SourceManager(), null);
}
return sourceManager;
}
}
}

Interlocked 类用于使变量的简单语句原子化。再用一个例子说明用 Interlocked 实现线程安全资源锁定机制。

在这个例子中,我们会建立10任务,每个任务会分别循环50000次请求使用资源,而这种资源我们限定同一时间只能有一个线程访问,请求成功则递增 accessed 值,失败则递增 denied 值,因此按我们的预期,accessed 和 denied 的和将会始终是 10*50000 = 500000。且看我们设计的机制:

    class InterlockedCase
{
private static int accessed = ;
private static int denied = ; // 0 没有线程在使用 1 有线程正在使用
private static int usingResource = ; private const int nTaskIterations = ;
private const int nTasks = ; public static void Test()
{
Task[] tasks = new Task[nTasks];
for (int i = ; i < nTasks; i++)
{
tasks[i] = Task.Factory.StartNew(ThreadProc);
}
for (int i = ; i < nTasks; i++)
{
tasks[i].Wait();
}
Console.WriteLine("accessed:{0}, denied:{1}, total:{2}", accessed, denied, accessed+denied);
} private static void ThreadProc()
{
for (int i = ; i < nTaskIterations; i++)
{
UseResource();
}
} private static bool UseResource()
{
if (usingResource == )
{
usingResource = ; accessed++; usingResource = ;
return true;
}
else
{
Interlocked.Increment(ref denied);
return false;
}
}
}

上面例子的运行结果total值却不是我们预期的总请求数 50000!

在代码中,我们设计了一个访问共享资源的逻辑

if (usingResource == 0)
{
  usingResource = 1;

  accessed++;

  usingResource = 0;
  return true;
}

错误的原因是我们控制资源的逻辑里 usingResource 的判断和赋值操作并不是原子操作,会导致有多个线程能同时进入内层操纵资源,修改 accessed!导致 accessed 值的统计不准确!

找到原因,我们把 usingResource 的判断和赋值转为原子操作,就能实现我们的构想了,Interlocked 类正好派上用场!

改造 UseResource() 函数,输出结果正式我们期望的 500000

        private static bool UseResource()
{
if (Interlocked.Exchange(ref usingResource,) == )
{
accessed++;
usingResource = ;
return true;
}
else
{
Interlocked.Increment(ref denied);
return false;
}
}

读到这里,有心的朋友可能会问,usingResource 变量为何设计成整型值?用布尔值不好吗?这正是体现整型值的灵活的地方,我们可以通过更改 UseResource() 函数的逻辑,控制统一时间可以有多少个线程访问资源,而并非只限定一个线程可以访问。

后话:

这是第二篇关于线程同步的学习笔记,其实书看得很快,但是文章却写得很慢。我发觉学习线程同步最好的方式就是设计一个反例,并更正它,确认运行结果是否与你预期的一致。在写这篇文章的过程中,我试图设计很多例子,也激发了自己很多的思考,其中有些想法开始是错的,在不断对比思考的过程里,逐渐加深认识了线程资源访问的设计。在大逻辑上,上一篇中 lock 语句会等待资源的释放,直至访问成功完成任务;而本篇中我们的线程会视图访问一些资源,不成功时我们会干别的事情,不会等待。

希望在这一系列文章写完的时候,我会对线程的同步有一个正确且深刻的认识,这也是我写这些读书笔记的目的。

C#线程同步技术(二) Interlocked 类的更多相关文章

  1. 【WIN32进阶之路】:线程同步技术纲要

    前面博客讲了互斥量(MUTEX)和关键段(CRITICAL SECTION)的使用,想来总觉不妥,就如盲人摸象一般,窥其一脚而言象,难免以偏概全,追加一篇博客查遗补漏. win32下的线程同步技术分为 ...

  2. 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock

    [源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...

  3. 转:C# 线程同步技术 Monitor 和Lock

    原文地址:http://www.cnblogs.com/lxblog/archive/2013/03/07/2947182.html 今天我们总结一下 C#线程同步 中的 Monitor 类 和 Lo ...

  4. iOS开发系列-线程同步技术

    概述 多线程的本质就是CPU轮流随机分配给每条线程时间片资源执行任务,看起来多条线程同时执行任务. 多条线程同时访问同一块资源,比如操作同一个对象.统一变量.同一个文件,就会引发数据错乱和数据安全的问 ...

  5. C#线程同步技术(一) lock 语句

    开篇语: 上班以后,烦恼少了,至少是没有什么好烦的了,只要负责好自己的工作就可以了,因此也有更多的时间去探索自己喜欢的程序.买回来的书已经看了一半,DEMO也敲了不少,昨晚终于在这里开BLOG,记录一 ...

  6. Delphi 线程同步技术(转)

    上次跟大家分享了线程的标准代码,其实在线程的使用中最重要的是线程的同步问题,如果你在使用线程后,发现你的界面经常被卡死,或者无法显示出来,显示混乱,你的使用的变量值老是不按预想的变化,结果往往出乎意料 ...

  7. (删)Java线程同步实现二:Lock锁和Condition

    在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...

  8. java线程池技术(二): 核心ThreadPoolExecutor介绍

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程池技术属于比较"古老"而又比较基础的技术了,本篇博客主要作用是个人技术梳理,没什么新玩意. 一.Java线程池技术的 ...

  9. JAVA线程同步 (二)notify()与notifyAll()-***

    编写多线程程序需要进行线程协作,前面介绍的利用互斥来防止线程竞速是来解决线程协作的衍生危害的.编写线程协作程序的关键是解决线程之间的协调问题,在这些任务中,某些可以并行执行,但是某些步骤需要所有的任务 ...

随机推荐

  1. [转]spring4.x注解概述

    1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能,因项目中用到不少注解,因此下定决心,经spring4.x中涉及到的注解罗列出来,供查询使用. 2. spring注解图     ...

  2. Winfrom 使用TabControl控件模拟程序向导步骤

    .NET : 隐藏TabControl的标签栏   在给应用程序添加一个向导的做法有很多,但其中比较简便易行的是使用TabControl.如下图所示 但有一个小小的美中不足,就是:作为向导而言,我们可 ...

  3. GCC 内联汇编(GCC内嵌ARM汇编规则)

    转:http://smileleeboo.howbbs.com/posts/list/3127/81062.html 更多文档参见:http://pan.baidu.com/s/1eQ7nd8Q 有时 ...

  4. Spring事务管理笔记

    事务的目的就是要保证数据的高度完整性和一致性. 在实际的项目中,大多都是使用注解的方式来实现事物,这里也就简单记录下使用@Transactional方法和注意事项. 在xml中添加配置 1234567 ...

  5. .NET Fframework

    .NET框架示意图: 该框架是微软推出的完全面向对象的软件开发与运行平台.其有两个主要 组将:CLR:公共语言运行库(Common Language Runtime,简称CLR)和.NET Frame ...

  6. 在安装python的mysqlclient包时报microsoft visual c++ 14.0 is required的错误

    在安装python的mysqlclient包时报microsoft visual c++ 14.0 is required的错误 pip install mysqlclient 提示报错   解决办法 ...

  7. stylus使用文档总结:内置方法+参数+条件+迭代+导入+继承

    一.内置方法 返回各种颜色的比重(如red(color)等) 颜色函数是CSS预处里器中内置的颜色函数功能,这些功能可以对颜色值进行处理,例如颜色的变亮.变暗.渐变颜色等处理十分的方便. lighte ...

  8. phantomjs 无法打开https网站解决方案

    最近测试原来的爬虫程序,发现phantomjs 无法打开https网站了,经过网上查下,发现需要在phantomjs定义的加以下参数 self.driver = webdriver.PhantomJS ...

  9. 《javascript高级程序设计》读书笔记(四)引用类型

    第五章:引用类型 Object类型 创建object实例的两种方式: 1.new方式 var person = new Object(); person.name = "haozk" ...

  10. Java笔记14:泛型初探

    一.泛型简介 泛型是从Java SE 1.5开始出现的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛 ...