【问题标题】:why "lock" keyword a object argument [duplicate]为什么“锁定”关键字对象参数[重复]
【发布时间】:2019-01-25 07:10:09
【问题描述】:

我不会说英语,我会使用翻译。

我在想什么时候学习线程同步。

class MainApp
{
    static public int count = 0;
    static private object tLock = new object();

    static void plus()
    {
        for (int i = 0; i < 100; i++)
        {
            lock (tLock)
            {
                count++;
                Console.WriteLine("plus " + count);
                Thread.Sleep(1);
            }
        }
    }

    static void minus()
    {
        for (int i = 0; i < 100; i++)
        {
            lock (tLock)
            {
                count--;
                Console.WriteLine("minus " + count);
                Thread.Sleep(1);
            }
        }
    }

    static void Main()
    {
        Thread t1 = new Thread(new ThreadStart(plus));
        Thread t2 = new Thread(new ThreadStart(minus));

        t1.Start();
        t2.Start();
    }
}

简单的线程学习。

静态私有对象 tLock = new object();

lock(tLock)

【问题讨论】:

  • 要在锁线程安全中创建语句?
  • 旁注:Console.WriteLine($"plus {Interlocked.Increment(ref count)}"); 是一种更快的实现方式(如果可能,请避免使用显式 lock
  • 这样你可能可以定义一个锁定对象,就像你发布的那样,而不是锁定要在同步下更新的实际对象......这可能会导致死锁
  • 你是在问为什么需要锁或者为什么需要提供一个对象作为它的参数?
  • 对不起。我想我写的问题太短了。已更正。

标签: c# multithreading synchronization locking


【解决方案1】:

为什么lock 上有一个对象参数?

好吧,因为它很方便。

首先,在您的代码示例中很明显,您需要在对lock 的调用之间共享一些状态,以声明两个不同的代码部分是互斥的。如果语法只是 lock { } 没有参数,像这样:

public void DoSomestuff()
{
    lock
    {
        // Section A
    }
}

public void DoOtherStuff()
{
    lock
    {
        // Section B
    }
}

那么要么所有的锁都是互斥的,要么只影响它们各自的代码部分(因此两个线程可以同时执行部分 A 和 B,但一次只有一个线程可以执行 A)。这会大大降低关键字的实用性。

既然我们已经确定我们需要一个共享状态,那么这个状态应该是什么?我们可以使用一个字符串:

lock ("My Section")
{
    // Section A
}

它可以工作,但有一些缺点:

  1. 您将自己暴露在不同库中不同部分的名称之间的潜在冲突中
  2. 这意味着运行时必须保留一种表来将字符串与锁相关联。没什么太难的,但这是一些开销

相反,.NET 作者使用对象参数。这解决了问题 1/,因为您知道另一个库不会引用您的对象,除非您愿意提供它。但这也解决了问题 2/,因为 这允许运行时将锁存储在实际的对象头中。这是一个非常巧妙的优化。

【讨论】:

  • 哇,这是一个完美的答案。谢谢。
【解决方案2】:

考虑以下(无锁):

    for (int i = 0; i < 1000; i++)
    {
        count++;
        Console.WriteLine("plus " + count);
        Thread.Sleep(1);
    }

如果两个线程同时运行:

  • 第一个线程将一个添加到count,现在是 1。
  • 现在第二个线程接管并将一个添加到count,现在是 2。
  • 第二个线程继续打印plus 2 并循环并再次向count 添加一个,现在是3。
  • 现在第一个线程接管并打印 plus 3,这不是预期的,因为在调用 WriteLinecount 为 1。

在添加锁定机制 (lock) 时,开发人员确保代码的一部分是 atomic,即按顺序运行而不会中断。

    for (int i = 0; i < 1000; i++)
    {
        lock (tLock)
        {
            count++;
            Console.WriteLine("plus " + count);
            Thread.Sleep(1);
        }
    }

如果您在此处遵循相同的模式:

  • 第一个线程将一个添加到count,现在是 1。
  • 现在第二个线程尝试接管,但必须等到lock 被第一个线程释放。
  • 第一个线程打印plus 1 并释放锁。
  • 现在第二个线程可以接管并将一个添加到count,现在是 2。
  • 第一个线程尝试接管,但必须等到第二个线程释放lock
  • 第二个线程打印plus 2 并释放锁。

如您所见,incrementWriteLine 现在是同步操作。

编辑

更改问题后:

lock 关键字需要 reference type 的对象。它不一定是object。它也可以是classinterfacedelegatedynamicstring

public static string a = string.Empty;
public static void Main()
{
    lock(a)
    {
        Console.WriteLine("Hello World");
    }
}

请参阅documentation 了解更多信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 2021-04-19
    • 2011-08-12
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多