为什么Lock语句无法按预期工作

时间:2019-10-17 22:31:35

标签: c# synchronization locking

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data                     -- in base
import Data.Generics.Uniplate.Data   -- package uniplate

data Expr
  = Variable String
  | Number Int
  | Add [Expr]
  | Sub Expr Expr
  deriving (Show, Data)

substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue = transform f
  where f (Variable x) | x == name = Number newValue
        f other = other

replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd = transform f
  where f (Sub x (Number y)) = Add [x, Number (-y)]
        f other = other

replaceSubWithAdd1 :: Expr -> Expr
replaceSubWithAdd1 = descend f
  where f (Sub x (Number y)) = Add [x, Number (-y)]
        f other = other

main = do
  print $ substituteName "x" 42 (Add [Add [Variable "x"], Number 0])
  print $ replaceSubWithAdd e
  print $ replaceSubWithAdd1 e
  where e = Add [Sub (Add [Variable "x", Sub (Variable "y") (Number 34)])
                     (Number 10), Number 4]

我花了很多时间试图理解为什么收到此消息: console result

有人可以告诉我这段代码有什么问题吗?

Mutex正常工作,但我也需要说明锁语句... 我希望在第一个线程中每次添加之后,我都会从第二个线程中获得一个收集状态。像这样:

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}");
}

1 个答案:

答案 0 :(得分:0)

我了解您认为这些威胁会在并行运行,但它们会顺序执行。您的期望是正确的。

但是,我认为这与锁没有任何关系。锁定只会阻止同时发生读取和写入,不会产生此行为。不带锁尝试尝试验证。 (但是由于JiT编译器,CPU缓存无效和优化之类的因素,即使有锁,即使没有直接影响,结果也可能会有所不同。)

我最好的选择是,读取线程是如此之慢,它不会在写入遍历所有对象之前完成一次。即使在控制台这样琐碎的事情上,编写UI也很昂贵。甚至尤其是那里。我使用robocopy备份了很多用户配置文件。而且,如果它碰到了很多非常小的文件,那么只要通过磁盘访问,只要编写控制台就成为 actuall程序瓶颈。而且,磁盘瓶颈问题通常不会经常发生。

如果每个用户触发的事件仅编写一次UI,您将不会注意到费用。但是可以从任何形式的循环中进行操作,尤其是在另一个线程中运行的循环中,您会开始注意到它。有人告诉我,foreach的迭代速度显然是for循环的一半。

尽管在Windows窗体环境中,我甚至为此举了一个例子:

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”写到控制台,而不是像这样的复杂东西怎么办?