算法设计与应用1-1 互斥算法_星野时雨的博客-程序员秘密

技术标签: 算法  java  算法设计与应用基础  

Chap 1

1.1互斥算法

进程vs线程

  • 计算机需要能够同时执行多个任务
  • 可以并行执行,也可以在执行过程中反复切换任务,或者两者混合执行
  • 重量级进程有自己的代码、数据内存(全局变量和堆空间)和执行堆栈(局部变量和方法调用的激活记录)
    • 进程可以独立运行,提供对外部资源(如文件)的访问是协调的(如由操作系统)
  • 一个线程的执行(轻量级进程)共享代码和数据内存,但有自己的执行堆栈
    • 线程可以在同一代码的不同点运行
    • 需要仔细协调对全局变量/字段的访问(共享状态),以避免竞争条件
  • 不同的应用程序通常在不同的进程中执行,但在应用程序中,线程通常用于执行多个任务
    • 操作系统需要时间在进程之间上下文切换(必须保存/恢复进程状态每个开关)
    • 在线程之间切换比在进程之间切换快得多

原子操作

  • 一个原子操作,保证一个线程在一瞬间完全执行

  • 没有其他线程可见的中间状态

  • 不是很明显什么操作是原子的,比如x++不是原子的,因为它可能变成多个机器指令

    LOAD R,x
    INC R
    STORE R,x
    
  • 每个线程都有自己的执行堆栈,所以使用相同寄存器变量R的两个线程没有问题(它们在交换线程时被保存/恢复)

非原子操作期间的线程交换

  • 如果线程通过非原子操作进行交换,可能会发生“奇怪的事情”(竞态条件)

  • 如果一个线程试图读取一个全局变量/字段,而另一个线程正在更改它的值,读取的值取决于线程交换执行时的精确时间

    • 场景1:线程A开始执行x++,但线程B在线程A存储新值之前读取了x(线程B获得了原始值)

      LOAD R,x
      INC R
      							LOAD R,x
      STORE R,x
      
    • 场景2:线程A执行x++,然后线程B随后读取x(线程B获得增量值)

      LOAD R,x
      INC R
      STORE R,x
      							LOAD R,x
      

更新丟失

  • 当两个线程修改相同的共享状态时,也会发生更糟糕的“奇怪的事情”

  • 示例:线程A执行x++,而线程B也执行x++

    • 场景1:线程A执行x++然后线程B执行x++ (x加2)

      LOAD R,x
      INC R
      STORE R,x
      							LOAD R,x
      							INC R
      							STORE R,x
      
    • 场景2:线程A开始执行x++,但是线程B在线程A存储新值之前读取了x (x只加1)

      LOAD R,x
      INC R
      							LOAD R,x
      							INC R
      							STORE R,x
      STORE R,x
      
  • 被称为“更新丟失问题”

    • 即使多个线程执行代码来更新共享状态,有时只出现一些更新

    • 调试可能非常具有挑战性

    • 示例代码LostUpdate

      /**
         A class that demonstrates the lost update problem in concurrency
         by creating two threads that concurrently try to increment x
         each a total of ITERATIONS times.
         Sometimes the final value of x is not 2*ITERATIONS
      */
      
      public class LostUpdate implements Runnable
      {
              
         private int x;
         private static final int ITERATIONS = 20000000; // multiple of 10
         
         public LostUpdate()
         {
                x = 0;
         }
         
         // repeatedly increment the value of x 
         public void run()
         {
                System.out.println("Thread " + Thread.currentThread()
               + " started with x = " + x);
            int loopIterations = ITERATIONS/10;
            for (int i=0; i<loopIterations; i++)
            {
                x++; x++; x++; x++; x++; x++; x++; x++; x++; x++;
            }
            System.out.println("Thread " + Thread.currentThread()
               + " finishing with x = " + x); 
         }
         
         public static void main(String[] args)
         {
                // create two concurrent threads
            LostUpdate lostUpdate = new LostUpdate();
            Thread threadA = new Thread(lostUpdate);
            Thread threadB = new Thread(lostUpdate);
            threadA.start();
            threadB.start();
            try
            {
                // wait for both threads to finish
               threadA.join();
               threadB.join();
            }
            catch (InterruptedException e)
            {
                System.out.println("Interrupted " + e);
            }
            System.out.println("The final value of x is " + lostUpdate.x);
         }
      }
      
      

临界区代码 Critical Sections of Code

  • 临界区是一个代码块,一次只能由一个线程执行
  • 临界区有访问或更新共享状态的代码
  • 互斥是指线程想要访问一个或多个使用相同共享状态的临界区
  • 强制线程在临界区段内执行代码之前获取锁
  • 在任何时刻,只有一个线程可以持有
  • 当线程不再需要在临界区段内执行代码时,线程必须释放锁
  • 互斥锁与共享状态相关
  • 每个共享状态可能有自己的锁,控制访问或更新共享状态的任何临界区

互斥算法的软件方法

互斥算法

  • ***互斥算法***是一种实现互斥的算法

    • 大大多数互斥锁算法会延迟“Acquire-Lock”中的线程,直到它被允许访问
    • 互斥锁算法在如何正确工作方面非常微妙
  • 如何不写互斥算法(因为它并不总是保证互斥对于线程t,使用一个名为***exclude***的布尔标志

    **Incorrect-Acquire-Lock(t)**
    1 while exclude do
    2 delay
    3 exclude <- true
    
    **Incorrect-Release-Lock(t)**
    1 exclude <- false
    
  • 简单地使用布尔标志可能无法进行测试,并且设置标志可能不是原子的

    • 一个线程可能完成while循环,然后在它设置exclude之前切换到另一个线程

Dekker的算法:单标志法

Dekker 's Algorithm是第一个正确处理(仅)两个线程的互斥的算法:

  • 为每个线程维护一个请求标志,当线程请求锁时设置该标志

  • 维护一个优先级变量,以指示在平局情况下,两个线程中接下来应该访问哪个线程

    **Dekker-Acquire-Lock(t)**
    1 s <- the other thread than t
    2 requested[t] <- true
    3 while requested[s] do
    4 if priority = s then
    5 requested[t] <- false
    6 while priority = s do
    7 delay
    8 requested[t] <- true
    
    **Dekker-Release-Lock(t)**
    1 s <- the other thread than t
    2 priority <- s 3 requested[t] <- false
    
    • 两个线程都可以并发调用Dekker-Acquire-Lock,但只有一个线程会返回
    • 另一个线程将延迟while循环,直到第一个线程调用Dekker-Release-Lock

皮特森算法/Peterson Algorithm

  • Peterson算法简化了Dekker算法

    • 为每个线程维护一个请求的标志字段,该字段在线程请求锁时设置
    • 维护一个回合变量来控制在平局情况下下一个线程必须等待
  • 在变量回合中利用竞争条件

    **Peterson-Acquire-Lock(t)**
    1 s <- the other thread than 
    2 requested[t] <- true
    3 turn <- t 4 while turn = t and requested[s] do
    5 delay
    
    **Peterson-Release-Lock(t)**
    1 requested[t] <- false
    
  • 可以修改为也工作在两个以上的线程(过滤算法)

    • 每个线程都将进入更高的级别,以便最终获得锁

兰波特面包店算法/ Lamport’s Bakery Algorithm

  • Lamport的bakery算法可以处理任意数量的线程

    • 每个想要访问的线程被分配一个数字
    • 数量增加,因为请求(但可能不是唯一的,如果竞争条件)
    • 线程的最低数字是下一个给定的访问权限
    • 每个线程有一个唯一的Id,以防领带
    • 为每个线程维护选择标志,以确保当前线程选择数字时不会提供锁
    Lamport-Acquire-Lock(t)
    1 choosing[t] <- true
    2 assign a number to thread t one larger than currently assigned maximum
    3 choosing[t] <- false
    4 for each thread s do
    5 while choosing[s] do
    6 delay
    7 while (num[s]≠0 and num[s]<num[t]) or
    (num[s]=num[t] and Id[s]<Id[t]) do
    8 delay
    Lamport-Release-Lock(t)
    1 num[t] <- 0
    

互斥锁算法的硬件辅助

  • 许多进程支持测试和设置指令

    • 硬件保证是原子的
    • 将内存位置设置为新值,返回原来的值
  • 原子测试和设置操作有助于创建简单的互斥锁算法

    • 维护排除标志
    TestAndSet-Acquire-Lock(t)
    1 while TestAndSet(exclude, true) do
    2 delay
    TestAndSet-Release-Lock(t)
    1 TestAndSet(exclude, false)
    

线程旋转、阻塞和等待

  • 最简单的延迟线程的方法是让它旋转(忙等待)时间
    • 线程在有处理器时间的时候反复检查条件,看看是否可以继续
      一旦线程有条件,它就会进行
    • 但是如果条件持续失败很长时间,线程会浪费处理器时间
  • 最好让线程块阻塞(睡眠)
    • 线程检查条件,如果不能进行,就放弃当前的时间片,这样其他线程就可以使用处理器时间
    • 将再次检查条件时,下一次分配的时间片
  • 最好但更复杂的解决方案可能是让线程等待
    • 线程检查条件,如果不能进行,它放弃当前的时间片,并被放入一个等待集
    • 线程不能做任何事情,没有得到处理器时间,直到它被从等待集删除
    • 需要与另一个线程协调,该线程可以通知自己何时从等待集中删除

更多reference

互斥:https://en.wikipedia.org/wiki/Mutual_exclusion

Leslie Lamport的面包店算法论文:http://lamport.azurewebsites.net/pubs/bakery.pdf

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41664510/article/details/120208561

智能推荐

checkio(一)_RICKC131的博客-程序员秘密

Multiply(Intro)Write a function that will receive 2 numbers as input and it should return the multiplication of these 2 numbers.Input: Two arguments. Both are of type int.Output: Int.Example:mult_two(2, 3) == 6mult_two(1, 0) == 0code:def mult_two(

JDBC工具类_JdbcUtils_jdbcutil工具类_Only MI的博客-程序员秘密

抽取JDBC工具类 : JDBCUtils目的:简化书写分析:注册驱动也抽取抽取一个方法获取连接对象需求:不想传递参数(麻烦),还得保证工具类的通用性。解决:配置文件jdbc.propertiesurl=user=password=抽取一个方法释放资源抽取注册驱动,抽取一个方法释放资源/** * JDBC工具类 */public class JDBCUtils { private static String url; privat

Kotlin笔记27--使用Intent传递数据_intent kotlin_南国樗里疾的博客-程序员秘密

接上一篇,使用 Intent 从 MainActivity 跳转到 FirstActivity ,不需要 FirstActivity 回传数据就用 startActivity,val intent = Intent(this, FirstActivity::class.java)intent.putExtra("key_from_main", "data_from_main")startActivity(intent)需要 FirstActivity 回传数据就用 startActivityF

曲线运动与万有引力公式_物质自旋与力的形成 ——关于万有引力与磁荷力本质与统一问题的探讨..._weixin_39592315的博客-程序员秘密

物质自旋与力的形成——关于万有引力与磁荷力本质与统一问题的探讨司 今(广州毅昌科技研究院 广州 510663 E-mail:[email protected])一、引言万有引力三百多年前,牛顿发现万有引力定律,即F=GMm/R²,这个公式首次揭示了一个场力与距离平方成反比的时代;一百多年后,库伦受万有引力定律和电荷概念的启迪,又分别发现了电荷、磁荷的场力定律,即F=ke q1 q2 /r²、F=...

团体程序设计天梯赛 L2-017. 人以群分_beforeevery的博客-程序员秘密

L2-017. 人以群分 时间限制 150 ms内存限制 65536 kB代码长度限制 8000 B判题程序 Standard 作者 陈越社交网络中我们给每个人定义了一个“活跃度”,现希望根据这个指标把人群分为两大类,即外向型(outgoing,即活跃度高的)和内向型(introverted,即活跃度低的)。要求两类人群的规模尽可能接近,而他们...

L2-017. 人以群分_s136424的博客-程序员秘密

L2-017. 人以群分时间限制150 ms内存限制65536 kB代码长度限制8000 B判题程序Standard作者陈越社交网络中我们给每个人定义了一个“活跃度”,现希望根据这个指标把人群分为两大类,即外向型(outgoing,即活跃度高的)和内向

随便推点

GIS相关资源归纳整理_osgearth gltf_熠熠仔的博客-程序员秘密

GIS相关源归纳整理1.GIS算法库2.GIS资料整理开源GIS图形引擎或小型GIS可视化库GIS数据解析三维地形服务GIS数据展示工具GIS数据处理矢量瓦片生成处理相关GIS数据其他3.其他内容1.GIS算法库Liang-Barsky线裁剪算法寻路算法流线算法几何数据处理(联合、相交等)3D几何网格数据压缩与解压自动拟合数字化曲线算法2D矩形空间索引的快速建立地理坐标库,支持坐标系的相互转换(WGS84,GCJ02,BD09等),支持GeoJSONproj4js地理坐标转换库基于K

L2-017 人以群分(25 分)_JimmyLegend的博客-程序员秘密

社交网络中我们给每个人定义了一个“活跃度”,现希望根据这个指标把人群分为两大类,即外向型(outgoing,即活跃度高的)和内向型(introverted,即活跃度低的)。要求两类人群的规模尽可能接近,而他们的总活跃度差距尽可能拉开。输入格式:输入第一行给出一个正整数N(2 &amp;lt;= N &amp;lt;=10^5^)。随后一行给出N个正整数,分别是每个人的活跃度,其间以空格分隔。题目保证这些数字以及它...

数据中台之流程引擎:Airflow详解_airflow流程_Freedom3568的博客-程序员秘密

一. 简介Airflow 是一个使用 Python 语言编写的 Data Pipeline 调度和监控工作流的平台。分布式任务调度框架Airflow 是通过 DAG(Directed acyclic graph 有向无环图)来管理任务流程的任务调度工具,不需要知道业务数据的具体内容,设置任务的依赖关系即可实现任务调度。这个平台拥有和 Hive、Presto、MySQL、HDFS、Postgres 等数据源之间交互的能力,并且提供了钩子(hook)使其拥有很好地扩展性。除了使用命令行,该工具还提供了一

Vue---10种组件传值方式总结,总有一款适合你_vue id property传值_绝世唐门三哥的博客-程序员秘密

本地缓存传值 localStorage/sessionStorage。router路由传参query/params。注入的方式传值 provide/inject。消息订阅与发表pubsub-js。路由组件方式传值[:xxx]vuex状态管理工具传值。

jsp页面中jstl标签详解_<c:out value="${lfn:escapehtml(sysorgperson.fdemai_-fly的博客-程序员秘密

JSLT标签库,是日常开发经常使用的,也是众多标签中性能最好的。把常用的内容,放在这里备份一份,随用随查。尽量做到不用查,就可以随手就可以写出来。这算是Java程序员的基本功吧,一定要扎实。 JSTL全名为JavaServer Pages Standard Tag Library,目前最新的版本为1.1版。JSTL是由JCP(Java Community Process)所制定的标准规范,

OCR图片预处理之去除印章(一)_ocr 图片预处理_修炼之路的博客-程序员秘密

导读import cv2import numpy as npdef remove_red_seal(input_img): # 通道分离 blue_c, green_c, red_c = cv2.split(input_img) # 选择阈值 thresh, ret = cv2.threshold(red_c, 0, 255,cv2.THRESH_TRIANGLE) # filter_condition = int(thresh * 0.9) fil

推荐文章

热门文章

相关标签