Потоково-безопасная замена для кода? - PullRequest
0 голосов
/ 30 января 2012

В процессе разработки я часто сталкиваюсь со следующей проблемой: если какой-либо метод уже выполняется одним потоком - метод не должен выполняться другим потоком.Другой поток ничего не должен делать - простой выход из метода, из-за этого я не могу использовать «блокировку».Обычно я решаю эту проблему следующим образом:

private bool _isSomeMethodExecuted = false;

public void SomeMethod ()
{
 if (!this._isSomeMethodExecuted) //check if method is already executed
 {
        this._isSomeMethodExecuted = true;

        //Main code of method

        this._isSomeMethodExecuted = false;
 }
}

Но этот код не является потокобезопасным: если один поток выполняет оператор условия, но он останавливается до установки флага в true, а другой поток может выполнять условие - тогдаоба потока находятся внутри кода метода.

Есть ли в нем замена для какого-либо потока?

Ответы [ 2 ]

2 голосов
/ 30 января 2012

следующее является поточно-ориентированным и не блокирует, если метод уже выполняется - даже если он уже выполняется в том же потоке ... что обеспечивает защиту от повторного входа для всех сценариев.

private long _isSomeMethodExecuted = 0;

public void SomeMethod ()
{
 if (Interlocked.Increment (ref this._isSomeMethodExecuted) == 1) //check if method is already executed
 {
        //Main code of method

 }
Interlocked.Decrement (ref this._isSomeMethodExecuted);
}

См. Ссылки http://msdn.microsoft.com/en-us/library/zs86dyzy.aspx

.
1 голос
/ 30 января 2012

Monitor выполняет эту работу за вас, но блокировка в потоке (и, следовательно, открыта для рекурсивных вызовов!). В операторе lock также используется Monitor (используется метод блокировки Enter), но вместо этого вы можете работать с методом TryEnter:

    if(Monitor.TryEnter(myLockObject))
    {
       try
       {
           DoSomething(); // main code
       }
       finally
       {
           Monitor.Exit(myLockObject);
       }
    }

TryEnter не блокирует, но возвращает bool, указывающий, была ли блокировка успешно получена или нет.

Если вы хотите, чтобы рекурсивные вызовы не вводили блок основного кода снова, вы должны использовать вместо этого семафор. Семафоры используют счетчики вместо блокировки объектов, поэтому вы не можете повторно войти даже из одного потока:

class Program
{
    private static Semaphore sem = new Semaphore(1, 1);

    static void Main(string[] args)
    {
        MyMethod();
        MyMethod();
    }

    private static void MyMethod()
    {
        if(sem.WaitOne(0))
        {
            try
            {
                Console.WriteLine("Entered.");
                MyMethod(); // recursive calls won't re-enter
            }
            finally
            {
                sem.Release();
            }
        }
        else
        {
            Console.WriteLine("Not entered.");
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...