【问题标题】:Concurrency and Thread safety when accessing variable访问变量时的并发性和线程安全
【发布时间】:2014-06-13 04:36:51
【问题描述】:

在编写多线程应用程序时,锁定类变量应该有多积极?例如,我有一个仅由一个线程更改的类字段(每秒运行一次),并由多个线程访问,这些线程复制对局部变量的引用,然后使用它,并且可以运行 1 毫秒。

我想知道我是否可以像这样使用对数据字段的引用:

public class OneThread {
    public static DataHolder Data = new DataHolder();
    public void CalledEverySecond() {
            Data = new DataHolder();
    }
}

public class MultiThread {
    public void RunsReallyOftenCalledFromMultipleThreads() {
        Data local = OneThread.Data;

        // now work with local reference

        // I suppose that it won't change and this is legal usage
        // i.e. I don't need to pollute this code with bunch of lock
        // or Monitor.Enter statements
    }
}

当然,我可以开始抛出 lock 和 Monitor 语句,但我想知道在这种情况下我是否真的需要它们。请仅在您能够提供代码并具有相当多的多线程编码经验的情况下回答 - 我不打算开始另一个讨论最佳实践和理论的线程。

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    这样使用本地引用似乎没有问题。我写了以下课程:

    public class DataHolder
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public static DataHolder InitRandom()
        {
            var random = new Random();
            return new DataHolder() {Id = random.Next(), Name = random.Next().ToString()};
        }
    }
    
    public class OneThread
    {
        public static readonly OneThread Instance = new OneThread();
    
        public OneThread()
        {
            Data = DataHolder.InitRandom();
        }
    
        public DataHolder Data;
        // this method is called often by one thread
        public void RunsEverySecond()
        {
            Data = DataHolder.InitRandom();
        }
    }
    
    public class MultiThread
    {
        public static List<string> Results = new List<string>(); 
    
        // called externaly by multiple threads
        public static bool SomeMethod()
        {
            var loc = OneThread.Instance.Data;
    
            // initializing new Random every time on purpose since that also takes time
            Thread.Sleep(new Random().Next(0,3));
    
            var id = loc.Id;
            var name = loc.Name;
    
            var res = loc.Id == id && loc.Name == name;
            if (!res)
                throw new Exception("different");
    
            var log = String.Format("{0} - OneThreadId: {1}, loc.Id: {2}, id: {3}", res, OneThread.Instance.Data.Id,
                loc.Id, id);
            lock (Results)
            {
                Results.Add(log);
            }
    
            return res;
        }
    
        public static void SpitResults()
        {
            File.WriteAllLines("C:\\res.txt", Results);
        }
    }
    

    为了测试,我创建了带有两个按钮的窗口窗体应用程序,用于初始化类并调用它们:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
    
            _secondTimer = new Timer(state => OneThread.Instance.RunsEverySecond(), null, 0, 2);
        }
    
        private Timer _secondTimer;
        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10000; i++)
            {
                ThreadPool.QueueUserWorkItem(o => MultiThread.SomeMethod());
            }
        }
    
        private void button2_Click(object sender, EventArgs e)
        {
            MultiThread.SpitResults();
        }
    }
    

    多次运行此测试后,我得到以下结果:

    True - OneThreadId: 968900563, loc.Id: 968900563, id: 968900563
    True - OneThreadId: 968900563, loc.Id: 968900563, id: 968900563
    True - OneThreadId: 1739428491, loc.Id: 968900563, id: 968900563
    True - OneThreadId: 1739428491, loc.Id: 968900563, id: 968900563
    True - OneThreadId: 1739428491, loc.Id: 968900563, id: 968900563
    True - OneThreadId: 1739428491, loc.Id: 968900563, id: 968900563
    True - OneThreadId: 1739428491, loc.Id: 968900563, id: 968900563
    True - OneThreadId: 1739428491, loc.Id: 1739428491, id: 1739428491
    True - OneThreadId: 1739428491, loc.Id: 1739428491, id: 1739428491
    True - OneThreadId: 1739428491, loc.Id: 968900563, id: 968900563
    

    所以,我的结论是,在这种情况下根本不需要主动锁定,如果我将类字段“复制”到局部变量,就不会有任何问题 - 那里的值不会改变,可以放心使用.

    如果您发现我的发现有任何错误,请发表评论/随时编辑。

    【讨论】:

      猜你喜欢
      • 2019-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多