编写多线程应用程序时,锁定类变量应该是多么激进?例如,我有一个只被一个线程更改的类字段(每秒运行一次),并且由多个线程访问,这些线程复制对局部变量的引用然后使用它并且可以运行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语句,但我想知道在这样的场景中我是否真的需要它们。请仅在您能够提供代码并且具有多线程编码经验时才能回答 - 我不打算开始另一个讨论最佳实践和理论的线程。
答案 0 :(得分:0)
使用像这样的本地引用似乎没有问题。我写过以下课程:
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);
}
}
为了测试,我创建了Window Form应用程序,其中包含两个初始化类并调用它们的按钮:
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
所以,我的结论是,在这样的情况下根本不需要积极锁定,如果我&#34;复制&#34;类字段到局部变量不会出现任何问题 - 这些值无法改变,可以毫无顾虑地使用。
如果您发现我的调查结果有任何错误,请发表评论/随时编辑。