如何在C#中告诉类方法当前是否正在执行

时间:2012-05-29 03:38:34

标签: c# multithreading

说我有以下代码(请假设所有适当的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功能,这会让我的生活变得更轻松。

4 个答案:

答案 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();
}