0

假设我有以下代码(请假设所有适当的导入语句):

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_1 完成执行,然后再继续 Method_2。

我同意我试图创建的最小工作示例并没有明确后一点。让我看看我是否可以编辑 MWE。

代码编辑完成

最终编辑

我正在使用 Visual Studio 2010 和 .NET 4.0,所以我没有可以让我的生活更轻松的异步/等待功能。

4

4 回答 4

0

首先 ConcurentDictionary 是线程安全的,因此您不需要锁定生产/消费。因此,如果只关心访问您的字典,则不需要额外的锁定。但是,如果您只需要相互排除方法 1 和 2 的执行,则应将锁定对象声明为类成员,并且可以使用它锁定每个函数体,但正如我所说,如果要使用 ConcurentDictionary,则不需要。如果你真的需要每时每刻都执行哪个方法,你可以使用每个线程的堆栈帧,但这会很慢,我相信这种情况没有必要。

于 2012-05-29T05:14:19.407 回答
0

您可以将其视为经典读者/作者问题的一种变体,其中读者不消费作者的产品。我认为你可以在一个 int 变量和三个 Mutex 的帮助下做到这一点。

一个 Mutex (mtxExecutingMeth2) 保护 Method2 的执行并阻止 Method2 和 Method1 的执行。Method1 必须立即释放它,否则您不能有 Method1 的其他并行执行。但这意味着当有 Method1 执行时,您必须告诉 Method2,这是使用 mtxThereAreMeth1 Mutex 完成的,只有在没有更多 Method1 执行时才释放该互斥锁。这由 numMeth1 的值控制,该值必须由另一个 Mutex (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();
} 
于 2012-05-29T06:20:38.047 回答
0

您正在寻找的术语是Thread Synchronization。在 .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; 
        }
    }
}
于 2012-05-29T05:33:28.517 回答
0

如上所述,您应该更加熟悉.net 中存在的 不同同步原语。
您不能通过反射或分析谁是并发运行方法来解决此类问题,而是通过使用信号原语,这将通知任何感兴趣的人该方法正在运行/结束。

于 2012-05-29T04:29:30.530 回答