繁体   English   中英

为什么 Lock 语句不能按预期工作

[英]Why Lock statement doesn't work as expected

static List<int> sharedCollection = new List<int>();
static readonly Object obj = new Object();
static void Main(string[] args)`enter code here`
{
  var writeThread = new Thread(() =>
  {
    for (int i = 0; i < 10; i++)
    {
      lock (obj)
      {
        Write();
      }
    }
  });

  var readThread = new Thread(() =>
  {
    for (int i = 0; i < 10; i++)
    {
      lock (obj)
      {
        Read();
      }
    }
  });

  writeThread.Start();
  readThread.Start();

  Console.ReadLine();
}

static void Read()
{
  Console.Write("Current collection state:  ");
  sharedCollection.ForEach((e) => Console.Write($"{e}  "));
  Console.WriteLine();
}

static void Write()
{
  Random generator = new Random();
  var addedValue = generator.Next(1, 20);
  sharedCollection.Add(addedValue);
  Console.WriteLine($"Added value is: {addedValue}");
}

我花了很多时间试图理解为什么我会收到这个:控制台结果

有人可以向我解释这段代码有什么问题吗?

互斥锁工作正常,但我也需要说明锁定语句......我希望在每次添加第一个线程后,我都会从第二个线程获得一个集合 state。 像这样:

Added value: 1
Collection state: 1
Added value: 15
Collection state: 1 15
Added value: 4
Collection state: 1 15 4

我了解您希望这些线程在某种程度上并行运行,但它们是按顺序执行的。 你的期望是正确的。

但是,我认为这与锁定无关。 lock 只会阻止读取和写入同时发生,不会产生这种行为。 不带锁试试看。 (但是,由于 JiT 编译器、CPU 缓存失效和优化等原因,如果有锁,结果可能仍然不同,即使它没有直接影响)。

我最好的选择是读取线程太慢了,在写入完成之前它不会完成一次 编写 UI 的成本很高,即使是在控制台这样微不足道的东西上也是如此。 甚至特别是那里。 我使用 robocopy 对用户配置文件进行了很多备份。 如果它遇到很多非常小的文件,那么仅仅编写控制台就会成为实际的程序瓶颈,超过磁盘访问 超出磁盘访问瓶颈的事情并不经常发生。

如果您只为每个用户触发的事件编写一次 UI,您将不会注意到成本。 但是从任何形式的循环中执行它——尤其是在另一个线程中运行的循环——你会开始注意到它。 我被特别告知,foreach 的运行速度显然是 for 循环的一半。

我什至为此做了一个例子,尽管在 Windows Forms 环境中:

using System;
using System.Windows.Forms;

namespace UIWriteOverhead
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        int[] getNumbers(int upperLimit)
        {
            int[] ReturnValue = new int[upperLimit];

            for (int i = 0; i < ReturnValue.Length; i++)
                ReturnValue[i] = i;

            return ReturnValue;
        }

        void printWithBuffer(int[] Values)
        {
            textBox1.Text = "";
            string buffer = "";

            foreach (int Number in Values)
                buffer += Number.ToString() + Environment.NewLine;
            textBox1.Text = buffer;
        }

        void printDirectly(int[] Values){
            textBox1.Text = "";

            foreach (int Number in Values)
                textBox1.Text += Number.ToString() + Environment.NewLine;
        }

        private void btnPrintBuffer_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Generating Numbers");
            int[] temp = getNumbers(10000);
            MessageBox.Show("Printing with buffer");
            printWithBuffer(temp);
            MessageBox.Show("Printing done");
        }

        private void btnPrintDirect_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Generating Numbers");
            int[] temp = getNumbers(1000);
            MessageBox.Show("Printing directly");
            printDirectly(temp);
            MessageBox.Show("Printing done");
        }
    }
}

但即使是这种开销也不太可能产生持久的结果。 在某些时候,读线程应该首先获得锁,阻塞写。 但是,仍然有太多变数无法确定。 您应该尝试一个更简单的示例,使用更一致(并且少得多)的书写工作。 将“A”和“B”写入控制台怎么样,而不是像这样复杂的东西?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM