使用锁对象的存储副本在多个线程中使用锁
Using lock in multiple threads with a stored copy of the lock object
我正在尝试对传递到我的工作线程的共享对象使用锁。在下面的代码中,如果我在 Worker 的 Execute 方法中传入 syncLock 对象,一切正常。
但是,如果我在我的 Worker class 中存储 syncLock 对象的本地副本,它就不起作用。
显然,当我执行“_syncLock = syncLock;”时赋值,而不是引用共享的 syncLock 对象,我得到了一个新对象。所以我现在每个线程都有自己的 syncLock 而不是共享对象。
有没有办法存储对共享对象的本地引用?我认为对象赋值在 C# 中始终是“引用”?
Worker.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Worker
{
int _ID;
string _Request;
object _syncLock;
MyCache _TheCache;
public Worker(int ID, string Request, ref object syncLock, ref MyCache TheCache)
{
_ID = ID;
_Request = Request;
_syncLock = syncLock;
_TheCache = TheCache;
}
public void Execute()
{
lock (_syncLock)
{
if (_TheCache == null)
{
Thread.Sleep(2000);
_TheCache = new MyCache();
_TheCache.LoadCache();
Console.WriteLine("thread {0}: created and loaded the cache", _ID);
}
else
{
Console.WriteLine("thread {0}: using the existing cache", _ID);
Console.WriteLine("TheCache.MyCacheValue {0}", _TheCache.MyCacheValue);
Console.WriteLine("TheCache.CacheTimeStamp {0}", _TheCache.CacheTimeStamp);
}
}
Console.WriteLine("worker DoSomething: {0}", _Request);
}
}
}
Main.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Main
{
public MyCache TheCache = null;
public object syncLock = new object();
public void Execute()
{
List<Task> TaskList = new List<Task>();
for (int i = 0; i < 10; i++)
{
Task _Task = new Task(() => DoSomething(i, "test"));
_Task.Start();
Console.WriteLine("started Thread={0} at {1}", _Task.Id, DateTime.Now.ToString("hh:mm:ss.fff tt"));
}
Console.WriteLine("Press any key to quit the program");
while (Console.ReadKey().KeyChar == 0) ;
}
void DoSomething(int ID, string Request)
{
Worker worker = new Worker(ID, Request, ref syncLock, ref TheCache);
worker.Execute();
}
}
}
MyCache.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class MyCache
{
public int MyCacheValue;
public DateTime CacheTimeStamp;
public MyCache()
{
}
public void LoadCache()
{
MyCacheValue = 1;
CacheTimeStamp = DateTime.Now;
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Program
{
static void Main(string[] args)
{
Main main = new Main();
main.Execute();
}
}
}
我解决了这个问题,感谢让我走上正轨的评论。特别要感谢 MickyD,他评论了需要 thread-safe 的代码,这让我找到了答案。
事实证明,我在我的 Execute 函数中使用锁进行了正确保护,但是在我的构造函数中,我仍然在没有锁的情况下引用共享缓存对象,这不是 thread-safe 并导致竞争条件。
解决方法是将构造函数调用和执行都放在关键 section/lock 块中,如下所示:
void DoSomething(int ID, string Request)
{
lock(syncLock)
{
Worker worker = new Worker();
worker.Execute(ID, Request, ref TheCache);
}
}
这也简化了工作代码,避免了存储 syncLock 对象的本地引用并在其中进行锁定的需要。
我也同意 MickyD 的其他评论,即这段代码应该 re-written 才能使用 async/await,在 worker 中没有缓存等等。我们使用这个代码库的寿命有问题,因此目前投资于技术债务是不确定的。
我正在尝试对传递到我的工作线程的共享对象使用锁。在下面的代码中,如果我在 Worker 的 Execute 方法中传入 syncLock 对象,一切正常。
但是,如果我在我的 Worker class 中存储 syncLock 对象的本地副本,它就不起作用。
显然,当我执行“_syncLock = syncLock;”时赋值,而不是引用共享的 syncLock 对象,我得到了一个新对象。所以我现在每个线程都有自己的 syncLock 而不是共享对象。
有没有办法存储对共享对象的本地引用?我认为对象赋值在 C# 中始终是“引用”?
Worker.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Worker
{
int _ID;
string _Request;
object _syncLock;
MyCache _TheCache;
public Worker(int ID, string Request, ref object syncLock, ref MyCache TheCache)
{
_ID = ID;
_Request = Request;
_syncLock = syncLock;
_TheCache = TheCache;
}
public void Execute()
{
lock (_syncLock)
{
if (_TheCache == null)
{
Thread.Sleep(2000);
_TheCache = new MyCache();
_TheCache.LoadCache();
Console.WriteLine("thread {0}: created and loaded the cache", _ID);
}
else
{
Console.WriteLine("thread {0}: using the existing cache", _ID);
Console.WriteLine("TheCache.MyCacheValue {0}", _TheCache.MyCacheValue);
Console.WriteLine("TheCache.CacheTimeStamp {0}", _TheCache.CacheTimeStamp);
}
}
Console.WriteLine("worker DoSomething: {0}", _Request);
}
}
}
Main.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Main
{
public MyCache TheCache = null;
public object syncLock = new object();
public void Execute()
{
List<Task> TaskList = new List<Task>();
for (int i = 0; i < 10; i++)
{
Task _Task = new Task(() => DoSomething(i, "test"));
_Task.Start();
Console.WriteLine("started Thread={0} at {1}", _Task.Id, DateTime.Now.ToString("hh:mm:ss.fff tt"));
}
Console.WriteLine("Press any key to quit the program");
while (Console.ReadKey().KeyChar == 0) ;
}
void DoSomething(int ID, string Request)
{
Worker worker = new Worker(ID, Request, ref syncLock, ref TheCache);
worker.Execute();
}
}
}
MyCache.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class MyCache
{
public int MyCacheValue;
public DateTime CacheTimeStamp;
public MyCache()
{
}
public void LoadCache()
{
MyCacheValue = 1;
CacheTimeStamp = DateTime.Now;
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Program
{
static void Main(string[] args)
{
Main main = new Main();
main.Execute();
}
}
}
我解决了这个问题,感谢让我走上正轨的评论。特别要感谢 MickyD,他评论了需要 thread-safe 的代码,这让我找到了答案。
事实证明,我在我的 Execute 函数中使用锁进行了正确保护,但是在我的构造函数中,我仍然在没有锁的情况下引用共享缓存对象,这不是 thread-safe 并导致竞争条件。
解决方法是将构造函数调用和执行都放在关键 section/lock 块中,如下所示:
void DoSomething(int ID, string Request)
{
lock(syncLock)
{
Worker worker = new Worker();
worker.Execute(ID, Request, ref TheCache);
}
}
这也简化了工作代码,避免了存储 syncLock 对象的本地引用并在其中进行锁定的需要。
我也同意 MickyD 的其他评论,即这段代码应该 re-written 才能使用 async/await,在 worker 中没有缓存等等。我们使用这个代码库的寿命有问题,因此目前投资于技术债务是不确定的。