механизм блокировки, который предоставляет информацию о состоянии в .NET 3.5 - PullRequest
1 голос
/ 24 июня 2010

Я пытаюсь найти способ предоставить эксклюзивный доступ к ресурсу, в то же время предоставляя информацию о состоянии блокировки (_isWorking) для чтения другими классами.

Вот что ядо сих пор придумали:

    private int _isWorking = 0;

    public bool IsWorking
    {
        get { return Interlocked.CompareExchange(ref _isWorking, 0, 0) == 1; }
    }

    public void LaunchProcess()
    {
        if (Interlocked.CompareExchange(ref _isWorking, 1, 0) == 0)
        {
            try
            {
                DoExpansiveProcess();
            }
            finally
            {
                Interlocked.Exchange(ref _isWorking, 0);
            }
        }
    }

    public void DoExpansiveProcess()
    {
        Thread.Sleep(30000);
    }

Он не работает точно как блокировка, поскольку второй поток, увидев, что поток уже выполняет процесс, просто выйдет из метода и ничего не сделает.

Это правильный способ сделать это или есть какая-то структура C # 3.5, более приспособленная для этой цели?

И как реализовать сценарий «реальной» блокировки, при котором второй поток все равно будет выполнятьсяметод после первого завершается, предоставляя информацию о состоянии?

Ответы [ 3 ]

1 голос
/ 24 июня 2010

Предполагая, что я правильно понимаю ваш вопрос, тогда я думаю, что вам нужен метод Monitor.TryEnter со значением тайм-аута 0. Он попытается получить блокировку, но при тайм-ауте 0 он всегда сразу вернетсязначение, указывающее, была ли блокировка фактически получена.Здесь подразумевается, что если блокировка не была получена, то предполагается, что она уже была получена кем-то другим.Проблема в том, что если возвращается true, то вам придется немедленно отпустить его с вызовом Monitor.Exit.

Возможно, это будет лучшим решением:

public class Worker
{
  private Object m_LockObject = new Object();
  private volatile bool m_IsWorking = false;

  public bool IsWorking
  {
    get { return m_IsWorking; }
  }

  public void LaunchProcess()
  {
    lock (m_LockObject)
    {
      m_IsWorking = true;
      DoExpansiveProcess();
      m_IsWorking = false;
    }
  }
}
0 голосов
/ 24 июня 2010

Я думаю, что ваш подход в целом должен быть правильным и определенно будет намного быстрее, чем использование ManualResetEvent (если оно вообще уместно) - потому что ManualResetEvent оборачивает базовый неуправляемый примитив.

Однако, чтобы сделать этот подход безопасным, _isWorking должен быть закрытым.

0 голосов
/ 24 июня 2010

System.Threading.ManualResetEvent может помочь здесь.Просто сделайте WaitOne () против него.Если он используется, он вернется как ложный.Если он не используется, он доступен.

Для одной из перегрузок WaitOne () требуется время ожидания в миллисекундах.Если вы используете WaitOne (0), ожидание немедленно вернется неблокирующим способом.

Вам следует провести некоторое исследование примитивов синхронизации, которые предлагает .Net:

System.Threading.Monitor lock() { }
System.Threading.ManualResetEvent
System.Threading.AutoResetEvent
System.Threading.Semaphore
System.Threading.Mutex
...