避免使用锁启动新线程

Avoid starting new thread with lock

是否可以锁定一个线程的方法并强制另一个线程继续执行而不是等到第一个线程完成?这个问题可以用静态线程或一些适当的模式来解决吗?下面提到的服务的一个实例。

出于演示目的,可以使用如下所示的静态布尔值来完成。

public class SomeService
{
  private readonly IRepository _repo;
  public SomeService(IRepository repo)
  {
    _repo = repo;
  }

  private Thread threadOne;

  public static bool isLocked { get; set; }

  public void StartSomeMethod()
  {
    if(!isLocked)
    {
      threadOne = new Thread(SomeMethod);
      isLocked = true;
    }
  }

  public void SomeMethod()
  {
    while(true)
    {
      lots of time
    }
    ...
    isLocked = false;
  }
}

我想避免用户不小心单击两次以启动并且意外地在第一个完成后立即启动第二个线程的情况。

你可以使用锁:)

object locker = new object();
void MethodToLockForAThread()
{
    lock(locker)
    {
      //put method body here
    }
}

现在的结果是,当线程(任何线程)调用此方法时,它会在锁的开头放置类似标志的内容:"STOP! You are not allowed to go any further, you must wait!" 就像十字路口的红灯。 当首先调用此方法的线程离开范围时,然后在范围的开头,此 "red light" 变为绿色。

如果你不想调用已经被另一个线程调用的方法,唯一的方法是使用 bool 值。例如:

object locker = new object();
bool canAccess = true;
void MethodToLockForAThread()
{
    if(!canAccess)
      return;

    lock(locker)
    {
      if(!canAccess)
        return;

      canAccess = false;            

      //put method body here

      canAccess = true;
    }
}

在锁定范围内对 canAccess 的其他检查是因为评论中提到的内容。不,它真的是线程安全的。这是线程安全单例中建议的一种保护。

编辑

经过与mjwills的一番讨论,我不得不改变主意,转为Monitor.TryEnter。你可以这样使用它:

object locker = new object();
void ThreadMethod()
{
  if(Monitor.TryEnter(locker, TimeSpan.FromMiliseconds(1))
  {
     try
     {
       //do the thread code
     }
     finally
     {
       Monitor.Exit(locker);
     }
  } else
    return; //means that the lock has not been aquired
}

现在,由于某些异常或其他线程已经获取锁,无法获取锁。在第二个参数中,您可以传递线程等待获取锁的时间。我在这里给了很短的时间,因为你不想让另一个线程来做这项工作,当第一个是这样做的时候。 所以这个解决方案似乎是最好的。

当其他线程无法获得锁时,它会继续等待而不是等待(好吧它会等待1毫秒)。

您可以使用 AutoResetEvent 代替 isLocked 标志。

AutoResetEvent autoResetEvent = new AutoResetEvent(true);
public void StartSomeMethod()
{
    if(autoResetEvent.WaitOne(0))
    {
        //start thread
    }
}

public void SomeMethod()
{
    try
    {
        //Do your work
    }
    finally
    {
        autoResetEvent.Set();
    }
}

由于 lockMonitor class 的特定语言包装器,因此您需要 Monitor.TryEnter:

public class SomeService
{
    private readonly object lockObject = new object();

    public void StartSomeMethod()
    {
        if (Monitor.TryEnter(lockObject))
        {
            // start new thread
        }
    }

    public void SomeMethod()
    {
        try
        {
            // ...
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }    
}