Как передать блокировки между потоками? - PullRequest
2 голосов
/ 09 февраля 2012

Я бы хотел

Monitor.Enter(this.StaticLock);
try
{
    // Do something ...

    ThreadPool.QueueUserWorkItem(state =>
    {
        try
        {
             // Do something else...

        }
        finally
        {
            Monitor.Exit(this.StaticLock);
        }
    });
}
catch (Exception)
{
    Monitor.Exit(this.StaticLock);
    throw;
}

Но это не работает, поскольку он не может Monitor.Exit для объекта, который не был Monitor.Enter в текущем потоке.Как это сделать?Должен ли я использовать межпоточную связь?

Ответы [ 5 ]

14 голосов
/ 09 февраля 2012

Как передать блокировки между потоками?

Вы вводите монитор в исходную ветку, скажем, в альфа-нить. Любые другие потоки, которые пытаются войти в монитор, будут блокироваться, пока монитор не будет доступен.

Если вы затем хотите перенести блокировку в другой поток, скажем, поток Bravo, при этом все еще имея возможность возобновить поток Alpha с владельцем монитора после завершения Bravo, вы помещаете Alpha в wait состояние на мониторе. Если нить Браво заблокирована на мониторе, то она просыпается и вводит монитор. Когда это сделано, он пульсирует монитор, который отказывается от владения Браво монитором и передает право собственности обратно в Alpha, которая просыпается и продолжает работать с владельцем монитора.

Если вам это совершенно не понятно, то (1) вам не следует пытаться делать это в первую очередь; это очень опасно, если вы ошиблись, и (2) вы должны прочитать это:

http://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystified

5 голосов
/ 09 февраля 2012

Semaphore s позволяют вам заблокировать их в одном потоке и разблокировать их в другом.

Но такое поведение очень подозрительно для меня ... Что именно вы пытаетесь достичь?Это практически никогда не должно быть сделано на практике.

static readonly Semaphore semaphore = new Semaphore(1, 1);

void Method1()
{
    semaphore.WaitOne();
    try
    {
        // Do something ...

        new Thread(() =>
        {
            try
            {
                // Do something else...

            }
            finally
            {
                semaphore.Release();
            }
        }).Start();
    }
    catch (Exception)
    {
        semaphore.Release();
        throw;
    }
}
5 голосов
/ 09 февраля 2012

Вы можете использовать мьютексы для этого.Они похожи на замки, но имеют немного больше возможностей.Они также дороже.

Как вы наверняка уже знаете, вы находитесь здесь на опасной территории.Передача замков через потоки опасна ...

1 голос
/ 09 февраля 2012

Одной из альтернатив является использование WaitHandle

var waitHandle = new AutoResetEvent(initialState: false);
ThreadPool.QueueUserWorkItem(state =>
{
    lock(this.staticLock)
    {
        try
        {
            // Do something ...

        }
        finally
        {
            waitHandle.Set();
        }

        // Do something else...

    }
}

waitHandle.WaitOne();
0 голосов
/ 09 февраля 2012

Если бы ответ Эрика Липперта мне подошел, я бы реализовал:

lock(this.StaticLock)
{
    ThreadPool.QueueUserWorkItem(state =>
    {
        lock(this.StaticLock)
        {
             // Do something ...

             Monitor.Pulse(this.StaticLock);
             // Do something else...

        }
    });
    Monitor.Wait(this.StaticLock);
}

К сожалению, это решение не предотвращает блокировку this другим потоком. Статическая блокировка между Monitor.Wait и блокировкой потока в очереди

...