C#多线程死锁介绍与案例代码

C#多线程死锁介绍与案例代码

死锁的概念

死锁(Deadlock)指的是多个线程因相互等待而陷入的一种僵局,每个线程都在等待其他线程释放资源。因此,所有线程都处于无法继续执行的状态,形成了死锁。

死锁产生的原因

死锁是由于多个线程相互等待对方所占用的资源而产生的。举例来说,有两个线程 A 和 B,他们需要占用相互持有的两个资源 R1 和 R2,但由于占用资源 R1 和 R2 的顺序不同,导致双方都在等待对方释放已经占用的资源,最终发生死锁。

死锁案例代码

下面是一个简单的死锁案例代码:

public class Account
{
    private readonly object _lockObject = new object();
    public int Balance { get; private set; }

    public void Deposit(int amount)
    {
        lock (_lockObject)
        {
            Balance += amount;
        }
    }

    public void Withdraw(int amount)
    {
        lock (_lockObject)
        {
            Balance -= amount;
        }
    }

    public void Transfer(Account destination, int amount)
    {
        lock (_lockObject)
        {
            destination.Deposit(amount);
            Withdraw(amount);
        }
    }
}

public class Test
{
    public static void Main()
    {
        Account account1 = new Account();
        account1.Deposit(100);

        Account account2 = new Account();
        account2.Deposit(100);

        Thread thread1 = new Thread(() =>
        {
            account1.Transfer(account2, 50);
        });

        Thread thread2 = new Thread(() =>
        {
            account2.Transfer(account1, 50);
        });

        thread1.Start();
        thread2.Start();

        thread1.Join();
        thread2.Join();

        Console.WriteLine("Account 1 Balance: " + account1.Balance);
        Console.WriteLine("Account 2 Balance: " + account2.Balance);
    }
}

在上述代码中,Account 类代表一个简单的银行账户,它有三个方法 Deposit、Withdraw 和 Transfer。其中,Deposit 和 Withdraw 方法用于分别向账户中存入和取出一定金额的钱,而 Transfer 方法用于从当前账户向目标账户转账。

而 Test 类的 Main 方法则创建了两个账户 account1 和 account2,每个账户分别存入了 100 元。

在创建两个线程 thread1 和 thread2 后,它们分别开始了钱的转账操作,其中 thread1 先将 50 元从 account1 转账到 account2 上,而 thread2 则将 50 元从 account2 转账到 account1 上。

然而,由于 Transfer 方法中先对目标账户进行锁定并执行 Deposit 操作后,才对当前账户进行锁定并执行 Withdraw 操作,因此在两个线程同时执行时,就会出现死锁的情况:假设 thread1 先占用了 account1 的锁,而 thread2 先占用了 account2 的锁,在互相等待对方释放锁的过程中,两个线程都无法继续执行,形成了死锁。

死锁的避免

避免死锁的方法主要有以下几种:

  • 按固定的顺序获取锁:通过约定一定的顺序,在多个线程中按照约定的顺序获取锁和释放锁,可以避免死锁的产生。
  • 使用内置锁:在 C# 中,使用 lock 关键字可以获得内置锁,它能够自动检测并在需要时等待获取锁,释放锁需要显式地调用 lock 代码块。
  • 使用超时机制:在获取锁时指定一个超时时间,在超时时间内无法获取到锁则进行回退,避免长时间等待而产生死锁。

示例说明

下面介绍一些死锁案例代码中避免死锁的实现方式:

按固定的顺序获取锁

通过定义账户的 ID,按照升序对他们进行排序,然后按照排序后的顺序获取锁和释放锁,可以有效避免死锁:

public class Account
{
    private readonly object _lockObject = new object();

    public int Id { get; private set; }
    public int Balance { get; private set; }

    public Account(int id, int balance)
    {
        Id = id;
        Balance = balance;
    }

    public void Deposit(int amount)
    {
        lock (_lockObject)
        {
            Balance += amount;
        }
    }

    public void Withdraw(int amount)
    {
        lock (_lockObject)
        {
            Balance -= amount;
        }
    }

    public void Transfer(Account destination, int amount)
    {
        if (Id < destination.Id)
        {
            lock (_lockObject)
            {
                lock (destination._lockObject)
                {
                    destination.Deposit(amount);
                    Withdraw(amount);
                }
            }
        }
        else
        {
            lock (destination._lockObject)
            {
                lock (_lockObject)
                {
                    Withdraw(amount);
                    destination.Deposit(amount);
                }
            }
        }
    }
}

在 Transfer 方法中,首先比较源账户和目标账户的 ID,如果源账户的 ID 小于目标账户的 ID,则先对源账户进行锁定再对目标账户进行锁定,否则就先对目标账户进行锁定再对源账户进行锁定。这种方式可以避免死锁的发生。

使用内置锁

使用 C# 中的 lock 关键字可以获得内置锁,它能够自动检测并在需要时等待获取锁,释放锁需要显式地调用 lock 代码块:

public class Account
{
    public int Balance { get; private set; }

    public void Deposit(int amount)
    {
        lock (this)
        {
            Balance += amount;
        }
    }

    public void Withdraw(int amount)
    {
        lock (this)
        {
            Balance -= amount;
        }
    }

    public void Transfer(Account destination, int amount)
    {
        lock (this)
        {
            lock (destination)
            {
                destination.Deposit(amount);
                Withdraw(amount);
            }
        }
    }
}

在 Transfer 方法中,对源账户和目标账户都使用 lock 关键字进行锁定,这样就能够保证在多个线程中不会出现竞争而导致死锁。

总之,避免死锁一直都是多线程编程中需要深入思考和处理的问题,开发人员需要注意锁的力度、锁的获取顺序、锁的防护范围等多个方面,从而避免死锁的发生。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#多线程死锁介绍与案例代码 - Python技术站

(0)
上一篇 2023年5月31日
下一篇 2023年5月31日

相关文章

  • c# 实现位图算法(BitMap)

    C# 实现位图算法(BitMap)攻略 什么是位图算法 位图算法(BitMap),也称为比特映射算法。是一种基于位运算的数据结构。 它的原理是把数据映射到包含这些数据的整数范围内,利用0和1的二进制方式来记录数据是否出现过。当数据量庞大时,时间复杂度远低于其他数据结构,所以在一些需要高效的场景中应用广泛。 例如,在搜索引擎的爬虫程序中,经常需要对已爬取的网页…

    C# 2023年6月8日
    00
  • MSScriptControl.ScriptControl组件的用法实例

    MSScriptControl.ScriptControl组件的用法实例 简介 MSScriptControl.ScriptControl是一个用于Windows开发的脚本控制器,在运行时可以解释和执行脚本代码。它支持多种脚本语言,如JavaScript、VBScript等,并提供了丰富的API接口,可用于实现各种脚本功能,是非常实用的开发工具。 安装 MS…

    C# 2023年5月31日
    00
  • 浅谈C#泛型的用处与特点

    浅谈C#泛型的用处与特点 什么是C#泛型? C# 泛型是一种类型参数化的技术,可以使用一个通用的方法或类来处理多种数据类型。通过使用泛型,可以编写更加灵活和可重用的代码,同时也可以提高代码的可维护性和可读性。 C#泛型的用处 提高代码的复用性 使用泛型可以编写更加通用的代码,可以处理多种类型的数据。不同于传统的方法和类,使用泛型可以更加直观和简单地完成类型的…

    C# 2023年5月15日
    00
  • ACCESS数据库修改自动编号的ID值为零的方法分享

    修改Access数据库自动编号为零的ID值的方法 在Access数据库中,如果自动编号(AutoNumber)字段的值为0,有时候需要进行修改。但是,Access默认情况下会将自动编号作为数据表的主键,因此直接修改自动编号的值可能会导致数据损坏。下面将详细介绍如何安全地修改Access数据库中自动编号为零的ID值。 步骤一:备份数据 在进行任何数据库操作之前…

    C# 2023年6月6日
    00
  • C#判断一个字符串是否包含另一个字符串的方法

    本文将为大家介绍C#中判断一个字符串是否包含另一个字符串的方法。 方法一:使用Contains()方法 使用C#的字符串类型中的Contains()方法,可以判断一个字符串是否包含其他某个字符串。该方法返回一个布尔值,如果被查询字符串包含在主字符串中,返回true;否则返回false。 string str1 = "hello world&quot…

    C# 2023年5月15日
    00
  • C#如何安全、高效地玩转任何种类的内存之Span的本质

    C#如何安全、高效地玩转任何种类的内存之Span的本质攻略 什么是Span Span<T> 是 .NET Core 中新增的一种类型,它是一种类似于指针的结构体,表示一块连续的内存区域,其内容不一定是类型T的连续区域,可以是其他原语类型(如byte、int、long等)的连续区间。Span<T> 可以让我们高效地访问和读写内存在不进行…

    C# 2023年5月15日
    00
  • JavaScript ESLint插件保姆级使用教程

    JavaScript ESLint插件保姆级使用教程 1. 什么是ESLint ESLint是一个可扩展的JavaScript代码检查工具。它可以检查代码中的语法错误,提供一致的代码风格,并可以检测代码中的潜在问题。ESLint 可以配置以满足您的特定需求。ESLint内置了很多规则,您也可以通过使用插件来添加自定义规则。 2. 安装ESLint 2.1 安…

    C# 2023年5月15日
    00
  • ASP.NET Core MVC中Tag Helpers用法介绍

    ASP.NET Core MVC中Tag Helpers用法介绍 什么是Tag Helpers? Tag Helpers是ASP.NET Core MVC中一种新的标记语言,它可以让开发人员以更加直观、HTML标签化的方式为视图提供数据和行为。通过使用Tag Helpers,开发人员可以生成更清晰、更易于维护的视图,同时还可以更轻松地扩展ASP.NET Co…

    C# 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部