说我有以下代码(请假设所有适当的import语句):
public class CTestClass {
// Properties
protected Object LockObj;
public ConcurrentDictionary<String, String> Prop_1;
protected System.Timers.Timer TImer_1;
// Methods
public CTestClass () {
LockObj = new Object ();
Prop_1 = new ConcurrentDictionary<String, String> ();
Prop_1.TryAdd ("Key_1", "Value_1");
Timer_1 = new System.Timers.Timer ();
Timer_1.Interval = (1000 * 60); // One minute
Timer_1.Elapsed += new ElapsedEventHandler ((s, t) => Method_2 ());
Timer_1.Enabled = true;
} // End CTestClass ()
public void Method_1 () {
// Do something that requires Prop_1 to be read
// But *__do not__* lock Prop_1
} // End Method_1 ()
public void Method_2 () {
lock (LockObj) {
// Do something with Prop_1 *__only if__* Method_1 () is not currently executing
}
} // End Method_2 ()
} // End CTestClass
// Main class
public class Program {
public static void Main (string[] Args) {
CTestClass TC = new CTestClass ();
ParallelEnumerable.Range (0, 10)
.ForAll (s => {
TC.Method_1 ();
});
}
}
我知道可以使用MethodBase.GetCurrentMethod,但是(没有用全局变量进行凌乱的簿记)是否可以在没有反射的情况下解决问题?
提前感谢您的协助。
修改
(a)纠正了LockObj范围内的错误
(b)通过解释添加一点(取自我下面的评论)
我已经更正了我的代码(在我的实际项目中)并将LockObj作为类属性。问题是,Method_2实际上是由System.Timers.Timer触发的,当它准备好触发时,很可能Method_1已经在执行。但在这种情况下,在继续使用Method_2之前等待Method_1完成执行非常重要。
我同意我尝试创建的最小工作示例并未明确说明后一点。让我看看我是否可以编辑MWE。
CODE编辑完成
一次最终编辑
我正在使用Visual Studio 2010和.NET 4.0,所以我没有async / await功能,这会让我的生活变得更轻松。
答案 0 :(得分:0)
如上所述,您应该更熟悉.net中存在的不同synchronization primitives。
你不是通过反思或分析这种问题来解决这些问题,而是通过使用信令原语,这将告知任何感兴趣的方法正在运行/结束的人。
答案 1 :(得分:0)
首先,ConcurentDictionary是线程安全的,因此您无需锁定生成/使用。因此,如果只关心访问您的字典,则无需额外锁定。 但是,如果您只需要相互排除方法1和2的执行,则应该将lock对象声明为类成员,并且可以使用它来锁定每个函数体,但正如我所说,如果您要使用ConcurentDictionary,则不需要。 如果你真的需要在每个时刻执行哪个方法,你可以使用每个线程的堆栈帧,但这将会很慢,我相信这种情况不是必需的。
答案 2 :(得分:0)
您正在寻找的术语是Thread Synchronisation。在.NET中有很多方法可以实现这一点
其中一个(lock
)你发现了。
一般而言,锁定对象应该可以被需要它的所有线程访问,并在任何线程尝试锁定它之前初始化。
lock()
语法确保一次只能为该锁定对象继续一个线程。尝试锁定同一对象的任何其他线程将停止,直到它们可以获得锁定。
无法超时或以其他方式取消等待锁定(除非终止线程或进程)。
举例来说,这是一个更简单的形式:
public class ThreadSafeCounter
{
private object _lockObject = new Object(); // Initialise once
private int count = 0;
public void Increment()
{
lock(_lockObject) // Only one thread touches count at a time
{
count++;
}
}
public void Decrement()
{
lock (_lockObject) // Only one thread touches count at a time
{
count--;
}
}
public int Read()
{
lock (_lockObject) // Only one thread touches count at a time
{
return count;
}
}
}
答案 3 :(得分:0)
您可以将此视为经典读者/作者问题的一种变体,其中读者不会使用作者的产品。我认为你可以借助int变量和三个Mutex来实现。
一个Mutex(mtxExecutingMeth2)保护Method2的执行并阻止Method2和Method1的执行。 Method1必须立即释放它,否则你无法进行Method1的其他并行执行。但是这意味着你必须告诉Method2什么时候有Method1正在执行,这是使用mtxThereAreMeth1 Mutex完成的,只有当没有更多的Method1执行时才会释放它。这由numMeth1的值控制,该值必须由另一个互斥锁(mtxNumMeth1)保护。
我没有试一试,所以我希望我没有介绍一些竞争条件。无论如何,它至少应该让你知道可能的方向。
这是代码:
protected int numMeth1 = 0;
protected Mutex mtxNumMeth1 = new Mutex();
protected Mutex mtxExecutingMeth2 = new Mutex();
protected Mutex mtxThereAreMeth1 = new Mutex();
public void Method_1()
{
// if this is the first execution of Method1, tells Method2 that it has to wait
mtxNumMeth1.WaitOne();
if (numMeth1 == 0)
mtxThereAreMeth1.WaitOne();
numMeth1++;
mtxNumMeth1.ReleaseMutex();
// check if Method2 is executing and release the Mutex immediately in order to avoid
// blocking other Method1's
mtxExecutingMeth2.WaitOne();
mtxExecutingMeth2.ReleaseMutex();
// Do something that requires Prop_1 to be read
// But *__do not__* lock Prop_1
// if this is the last Method1 executing, tells Method2 that it can execute
mtxNumMeth1.WaitOne();
numMeth1--;
if (numMeth1 == 0)
mtxThereAreMeth1.ReleaseMutex();
mtxNumMeth1.ReleaseMutex();
}
public void Method_2()
{
mtxThereAreMeth1.WaitOne();
mtxExecutingMeth2.WaitOne();
// Do something with Prop_1 *__only if__* Method_1 () is not currently executing
mtxExecutingMeth2.ReleaseMutex();
mtxThereAreMeth1.ReleaseMutex();
}