C#笔试题之同线程Lock语句递归不会死锁

前几天在网上闲逛,无意中看到有这么一道题及其答案,如下:

根据线程安全的相关知识,分析以下代码,当调用test方法时i>10时是否会引起死锁?并简要说明理由。

public void test(int i)
 {
  lock(this)
  {
   if (i > 10)
   {
     i--;
     test(i);
   }
  }
 }

答:不会发生死锁,(但有一点int是按值传递的,所以每次改变的都只是一个副本,因此不会出现死锁。但如果把int换做一个object,那么死锁会发生)

当我看到这道题时,我心里只有两个答案,1、会发生死锁,2、不会。^_^说了当没说。我觉得会发生死锁的理由是:同一线程只能进入lock语句一次,如果这个线程没有退出lock语句就不能再次进入lock语句。而不会发生死锁的理由是,同一线程可以多次进入到lock语句中。

我将这段代码拷入VS中运行,发现没有进入死锁,于是想找个权威的理由来解释它,终于在《CLR via C#》第二版(中文版,清华大学出版社出版)的第530页中第7行找到了这样的描述:“同样需要引起注意的是线程可以递归拥有同步块”。即同一线程可以递归调用lock语句。

以上只讨论了单线程的情况,下面的代码给出的两个线程的情况:

using System;
using System.Threading;

namespace LockDemo
{
  class Program
  {
    static void Main(string[] args)
    {
      Program p = new Program();
      MyObj obj = new MyObj();
      //第一个线程
      Thread thread1 = new Thread(p.test);
      thread1.Name = "thread1";
      //第一个线程
      Thread thread2 = new Thread(p.test);
      thread2.Name = "thread2";
      //启动线程
      thread1.Start(obj);
      thread2.Start(obj);
      Console.Read();
    }

    public void test(object obj)
    {
      lock (this)
      {
        if (((MyObj)obj).value > 10)
        {
          ((MyObj)obj).value--;
          Console.Write(Thread.CurrentThread.Name + ":");
          Console.WriteLine(((MyObj)obj).value);
          Thread.Sleep(10);
          test(obj);
        }
        else
        {
          Console.WriteLine(Thread.CurrentThread.Name);
        }
      }
    }
  }
  /// <summary>
  /// 将一个值类型封装在一个类中,以便多个线程调用方便
  /// </summary>
  public class MyObj
  {
    public int value;

    public MyObj()
    {
      //将初始值赋为20
      value = 20;
    }
  }
}

下面是运行结果:

由于thread1先进入lock语句,所以锁一直由thread1占有,递归调用直到不满足条件为止,thread1释放锁后,thread2进入lock语句时,发现当前已经不满足递归条件了,即:i < 10了,所以直接退出。

让我觉得奇怪的是网上给出的答案,即括号中的文字说明,明明代码中是对this对象加的锁,与传递的参数何关?找个int是按值传递的理由解释不会发生死锁让我觉得很奇怪。

注:如有不明白lock的背后技术原理的,请参考《CLR via C#》一书。

参考文献:《CLR Via C#》第二版,第530页,清华大学出版社

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#中实现线程同步lock关键字的用法详解

    1. lock关键字保证一个代码块在执行的过程中不会受到其他线程的干扰,这是通过在该代码块的运行过程中对特定的对象加互斥锁来实现的. 2. lock关键字的参数必须是引用类型的对象.lock对基本数据类型如int,long等无效,因为它所作用的类型必须是对象.如果传入long类型数据,势必被转换为Int64结构类型,则加锁的是全新的对象引用.如果需要对它们进行互斥访问限制,可以使用System.Threading.Interlocked类提供的方法,这个类是提供原子操作的. 3. lock(th

  • C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法

    本文实例讲述了C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法.分享给大家供大家参考,具体如下: 摘要:C#提供了System.Threading.ReaderWriterLock类以适应多用户读/单用户写的场景.该类可实现以下功能:如果资源未被写操作锁定,那么任何线程都可对该资源进行读操作锁定,并且对读操作锁数量没有限制,即多个线程可同时对该资源进行读操作锁定,以读取数据. 使用Monitor或Mutex进行同步控制的问题:由于独占访问模型不允许任何形式的

  • 描述C#多线程中lock关键字的使用分析

    本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待.每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数.这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生.C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一

  • C#使用Interlocked实现线程同步

    通过System.Threading命名空间的Interlocked类控制计数器,从而实现进程 的同步.Iterlocked类的部分方法如下表: 示例,同时开启两个线程,一个写入数据,一个读出数据 代码如下:(但是运行结果却不是我们想象的那样) using System; using System.Threading; namespace 线程同步 { class Program { static void Main(string[] args) { //缓冲区,只能容纳一个字符 char bu

  • c#多线程中Lock()关键字的用法小结

    本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待. 每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数.这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生. 其中,lock是一种比较好用的简单的线程同步方式,它是通过为给定对象获取互斥锁来实现同步的.它可以保证当一个线程在关键

  • C#笔试题之同线程Lock语句递归不会死锁

    前几天在网上闲逛,无意中看到有这么一道题及其答案,如下: 根据线程安全的相关知识,分析以下代码,当调用test方法时i>10时是否会引起死锁?并简要说明理由. public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } 答:不会发生死锁,(但有一点int是按值传递的,所以每次改变的都只是一个副本,因此不会出现死锁.但如果把int换做一个object,那么死锁会发生) 当我看到这道题时,我心里只有两个答案,1.

  • 2021最新Android笔试题总结美团Android岗职能要求

    目录 Android开发面试的几部分 1.基础知识 Java部分: Android部分: 数据结构与算法: 计算机基础: 设计模式: 开源项目: 重点项目经历 技术以外的东西 自我驱动和追求 沟通和协作 我的面经总结 Android Java 计算机网络 数据结构及算法 题外话 优秀的战士需要出色的剑才能战斗.同样,在现代IT中,每个编码人员都需要最好的Android开发人员工具来提高他们的技能和效率.在Android应用程序开发这个残酷的竞争行业中,只有优秀的开发人员才能生存下去.您需要向客户

  • Spring,hibernate,struts经典面试笔试题(含答案)

    本文讲述了Spring,hibernate,struts经典面试笔试题及其参考答案.分享给大家供大家参考,具体如下: 1.Hibernate工作原理及为什么要用? 原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建SessionFactory 3.打开Sesssion 4.创建事务Transation 5.持久化操作 6.提交事务 7.关闭Session 8.关闭SesstionFactory 为什么要用: ① . 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代

  • 华为2019校招笔试题之处理字符串(python版)

    说明 华为2019在线笔试题,现整理如下,以供之后参考 GitHub 题目介绍 ################################################################# ################################################################# ''' 题目描述: -- 对输入字符串检查是否存在非法字符,输出合法字符串(去重)和非法字符串(不去重) -- 对合法字符串循环左移10次,在进行排序输出.

  • JS前端笔试题分析

    本文实例分析了JS前端笔试题.分享给大家供大家参考,具体如下: 1.如何根据逗号分隔的字符串创建数组呢?请为下面的字符串创建一个数组,并访问第三个元素:"cats,dogs,birds,horses" 知识点:数组和字符串的转换.考察split() 方法.把一个字符串分割成字符串数组(将字符串按某个字符切割成若干个字符串,并以数组形式返回) var animalString="cats,dogs,birds,horses"; var animalArray=anim

  • JS经典正则表达式笔试题汇总

    本文实例总结了JS经典正则表达式笔试题.分享给大家供大家参考,具体如下: 一.复习字符串的传统操作 如何获取一个字符串中的数字字符,并按数组形式输出,如 dgfhfgh254bhku289fgdhdy675gfh 输出[254,289,675] 分析:循环用charAt()的方法获取到每一个子字符串,判断他是不是在0~9之间,是就把他扔到准备好的数组里 var str="dgfhfgh254bhku289fgdhdy675gfh"; findNum(str); function fin

  • 基于C++的拼多多算法在线笔试题示例

    本文实例讲述了基于C++的拼多多算法在线笔试题.分享给大家供大家参考,具体如下: 最近在狼厂实习中,很久没做题了.秋招第一发, 拼多多...  四个简单题,看到有些人竟然觉得难? 我来降一发自己的RP,这题目觉得难的,如果你拿到比我好的Offer,我是不服气的.. 四个题...其实我也就写了40分钟吧..不过最后也没有满分, 390/400, 第三题不知道为嘛一直有10分过不了.. 更一下, 刚刚好像发现第三题...这个>号, 我写的是>= ....? 可是我看题目好像是 >= 呀...

  • 华为校园招聘上机笔试题 扑克牌大小(python)

    本文为大家分享了华为校园招聘上机笔试题,供大家参考,具体内容如下 [编程题] 扑克牌大小 时间限制:10秒 空间限制:131072K 扑克牌游戏大家应该都比较熟悉了,一副牌由54张组成,含3~A,2各4张,小王1张,大王1张. 牌面从小到大用如下字符和字符串表示(其中,小写joker表示小王,大写JOKER表示大王):)  3 4 5 6 7 8 9 10 J Q K A 2 joker JOKER  输入两手牌,两手牌之间用"-"连接,每手牌的每张牌以空格分隔,"-&quo

  • C语言八道笔试题精讲带你掌握指针

    目录 题目一 题目二 题目三 题目四 题目五 题目六 题目七 题目八 为了题目的准确性和我们一般学习过程中的习惯,这里所有的题目代码都是在 X86 环境(32 位平台)下运行的. 题目一 #include <stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1)); return 0; } /

  • C语言常见的指针笔试题解析

    目录 笔试题1 笔试题2 笔试题3 笔试题4 笔试题5 笔试题6 笔试题7 笔试题8 在我们学习指针之后,应该在实际应用中去理解和掌握它,毕竟实践才是检验真理的唯一标准,我们以后在找工作的过程中免不了会遇到与指针相关的试题,本篇文章可以帮助我们提前了解一些常见的指针考点.在学习这篇文章之前可以根据需要对指针进行简要复习. 注:本篇文章所有代码均在X86环境下运行. 笔试题1 #include <stdio.h> int main() { int a[5] = { 1,2,3,4,5 }; in

随机推荐