.net 如何测试对象是否被锁定,如果没有,如何在将要调用的任务中使用它?

57hvy0tb  于 2022-11-19  发布在  .NET
关注(0)|答案(1)|浏览(163)

我需要做一些类似下面的代码。注意,由于.Net中的锁的性质(我将在代码之后解决这个问题),这不起作用,但我需要某种形式的实现,像这样的工作,但我不知道该怎么做。

static object lockMethod = new object();
static object lockTask = new object();

public static string testLocksAndTasks()
{
  //This method is fast executing code but to prevent other issues, I
  //lock this method.  This might be impertinent to the base need of this 
  //question, but since I do use a lock on the entire requesting method, I 
  //put it here.  This is because I integrate some logging and a bit of 
  //other logic on static variables so I just lock the method.  This entire 
  //function is fast.  The really slow code is in 
  //DoWorkInOtherThreadMethod(), which is why it needs to run in a 
  //background task.
  lock (lockMethod)
  {
    if (Monitor.TryEnter(lockTask))
    {
      Task.Run(DoWorkInOtherThreadMethod)
        .ContinueWith(ct=>
          Monitor.Exit(lockTask)); //Oop! Unlocking on separate task NOT ALLOWED!

      return "We locked lockTask and started the thread!";
    }
    else return "Task called and executed by other request!";
  }
}

上面的代码清楚地说明了我要完成的任务。(从Web请求执行),我需要测试从请求启动的任务是否已经在另一个线程中运行。(本例中为lockTask),并在运行时阻止将来的调用,并向调用方报告ack后台任务的状态。如果lockTask已在使用中,我特别需要返回“Task X is already running for Y item”。(注意,我没有包括已经在运行的任务的额外信息,但这并不难做到,并且本示例不需要。)
我的代码理论上可以工作,因为一旦整个任务完成,对ContinueWith()的调用将为任务解锁。但是,我发现这会引发错误,因为lockTask上的初始锁是在一个线程中创建的,然后在另一个线程上使用Monitor.Exit(lockTask进行后续解锁,这是不允许的。
我可以尝试将我的代码重组为下面提供的代码,但这也有我需要的问题。

public static string testLocksAndTasks()
{
  lock (lockMethod)
  {
    //Check if we're locked!
    if (!Monitor.IsEntered(lockTask))
    {
      Task.Run(()=>
        {
          //We weren't locked, so TryEnter...
          if (Monitor.TryEnter(lockTask))
          {
            DoWorkInOtherThreadMethod();
            Monitor.Exit(lockTask);  //NOTE: I KNOW THIS SHOULD BE WRAPPED IN A 
                                     //TRY/CATCH/FINALLY.  I'm just keeping sample 
                                     //the code simple.
          }
          else
          {
            //Oh no!  This is actually quite possible, but this is a case I never 
            //want to reach?!  I can't report this back to the initial call to 
            //testLocksAndTasks since we are in a new thread!
          }
        });

      return "We locked lockTask and started the thread!";
    }
    else return "Task called and executed by other request!";
  }
}

我在上面的评论中概括了一个明显的问题。我可以在任务中创建我的任务锁,这解决了锁和退出发生在不同线程上的第一个问题。然而,这引入了一个不同的但仍然重要的问题。
由于这段代码可以在多个请求线程中半同时执行,因此可能会有多个对!Monitor.IsEntered(lockTask)的调用返回true,因为直到在新任务中发出Monitor.TryEnter(...)请求时才设置锁。这不是问题,但是,我现在无法向testLocksAndTasks()返回正确的状态响应。
如何正确地实现这样的功能,即锁定长时间运行的任务,同时报告它是否正在运行?

sczxawaw

sczxawaw1#

听起来你不需要锁,而是需要一个简单的标志来指示任务是否正在运行。现在要读取和设置这个标志,你可以使用常规锁。所以你检查标志,并在必要时在锁内启动任务。当任务完成时,你再次在锁内设置标志。示例代码:

static object lockTask = new object();
private static bool taskRunning = false;
public static string testLocksAndTasks()
{
    lock (lockTask)
    {
        // check inside the lock
        if (!taskRunning)
        {
            Task.Run(DoWorkInOtherThreadMethod)
                .ContinueWith(ct =>
                {
                    lock (lockTask)
                    {
                        // reset flag inside the lock
                        taskRunning = false;
                    }
                });
            // start task and set the flag
            taskRunning = true;
            return "We locked lockTask and started the thread!";
        }
        else return "Task called and executed by other request!";
    }
}

相关问题