C#锁构造误解(带有msdn代码示例)

时间:2018-07-04 20:28:36

标签: c# multithreading

我对使用C#lock构造的一件事很感兴趣 现在从MSDN中取样,然后经过以下主要问题:

以下示例使用线程和锁。只要存在lock语句,该语句块就是关键部分,并且余额永远不会成为负数。

class Account
{
    private Object thisLock = new Object();
    int balance;

    Random r = new Random();

    public Account(int initial)
    {
        balance = initial;
    }

    int Withdraw(int amount)
    {

        // This condition never is true unless the lock statement
        // is commented out.
        if (balance < 0)
        {
            throw new Exception("Negative Balance");
        }

        // Comment out the next line to see the effect of leaving out 
        // the lock keyword.
        lock (thisLock)
        {
            if (balance >= amount)
            {
                Console.WriteLine("Balance before Withdrawal :  " + balance);
                Console.WriteLine("Amount to Withdraw        : -" + amount);
                balance = balance - amount;
                Console.WriteLine("Balance after Withdrawal  :  " + balance);
                return amount;
            }
            else
            {
                return 0; // transaction rejected
            }
        }
    }

    public void DoTransactions()
    {
        for (int i = 0; i < 100; i++)
        {
            Withdraw(r.Next(1, 100));
        }
    }
}

class Test
{
    static void Main()
    {
        Thread[] threads = new Thread[10];
        Account acc = new Account(1000);
        for (int i = 0; i < 10; i++)
        {
            Thread t = new Thread(new ThreadStart(acc.DoTransactions));
            threads[i] = t;
        }
        for (int i = 0; i < 10; i++)
        {
            threads[i].Start();
        }

        //block main thread until all other threads have ran to completion.
        foreach (var t in threads)
            t.Join();
    }
}

我不明白为什么使用lock帐户余额不会减少;它总是以0余额结束编译。    P.S对不起,我的英语。

3 个答案:

答案 0 :(得分:1)

该锁将按本示例中的预期工作,一旦启动多个线程,它们都会尝试从帐户中撤出,这可能会导致功能异常。怎么样?
好吧,想象一下当前余额为40,线程1尝试撤回40,线程2尝试撤回20,如果没有锁,它们都会成功完成,余额为-20,这是不可接受的。 / p>

由于您的实际顾虑,为什么余额没有变为负数?简单:

lock (thisLock)
    {
        if (balance >= amount)
        {
            Console.WriteLine("Balance before Withdrawal :  " + balance);
            Console.WriteLine("Amount to Withdraw        : -" + amount);
            balance = balance - amount;
            Console.WriteLine("Balance after Withdrawal  :  " + balance);
            return amount;
        }
        else
        {
            return 0; // transaction rejected
        }
    }

锁将确保仅当余额中有可用金额时每个线程才会退出,因此条件if (balance >= amount)if (balance < 0)结合使用将确保余额不会为负。

如果您记录每个线程提取的金额,则可以查看详细信息:

Console.WriteLine(Withdraw(r.Next(1, 100)));

您会看到其中的许多将在一段时间后输出0,因为该帐户不再具有余额,因此return 0会触发。

答案 1 :(得分:0)

您正在生成十个线程,它们几乎同时启动并同时运行。与减量操作相比,Console.WriteLine调用比较耗时,因此如果没有lock,则多个线程可以并且将进入由if (balance >= amount)保护的语句块(因为可能仍然有足够的平衡)到那时)同时,其他任何线程都将要到达并执行balance = balance - amount语句。

(没有Console.WriteLine调用,问题是相同的-比赛的可能性可能更低,但是除非关键部分是原子操作,否则仍然需要lock)。

使用lock时,不会有两个线程同时进入临界区,因此,如果在输入临界区时余额已经为零,它将安全地分支到{{ 1}}块中显示“交易被拒绝”。

我认为,一旦您意识到这是锁语法的真正目的,您的误解就将得到解决-防止对需要序列化读和/或写访问的资源进行竞争(这里的资源是balance变量) )。从那里开始,下一个挑战是else关键字documented here

答案 2 :(得分:-1)

我不确定100%,但是我假设MSDN提供的示例以类似于生产者-消费者的模式展示了这一点。如果没有lock,则多个线程在同一个数据上运行(即balance变量)可能会无意间出错。 lock确保在任何给定时间只有一个线程可以读写balance