527 Stimmen

Warum kann ich den 'await'-Operator nicht innerhalb des Körpers einer Sperranweisung verwenden?

Das Schlüsselwort await in C# (.NET Async CTP) ist innerhalb einer Sperranweisung nicht zulässig.

Desde MSDN :

Eine await-Ausdruck kann nicht verwendet werden in einer synchronen Funktion, in einer Abfrage Ausdruck, im catch- oder finally-Block einer Ausnahmebehandlung Anweisung, im Block einer Sperranweisung oder in einem unsicheren Kontext.

Ich nehme an, dass dies für das Compiler-Team entweder schwierig oder aus irgendeinem Grund unmöglich zu implementieren ist.

Ich habe versucht, mit der using-Anweisung eine Lösung zu finden:

class Async
{
    public static async Task<IDisposable> Lock(object obj)
    {
        while (!Monitor.TryEnter(obj))
            await TaskEx.Yield();

        return new ExitDisposable(obj);
    }

    private class ExitDisposable : IDisposable
    {
        private readonly object obj;
        public ExitDisposable(object obj) { this.obj = obj; }
        public void Dispose() { Monitor.Exit(this.obj); }
    }
}

// example usage
using (await Async.Lock(padlock))
{
    await SomethingAsync();
}

Dies funktioniert jedoch nicht wie erwartet. Der Aufruf von Monitor.Exit innerhalb von ExitDisposable.Dispose scheint auf unbestimmte Zeit zu blockieren (die meiste Zeit), was zu Deadlocks führt, da andere Threads versuchen, die Sperre zu übernehmen. Ich vermute, dass die Unzuverlässigkeit meines Workarounds und der Grund, warum await-Anweisungen in Lock-Anweisungen nicht zulässig sind, irgendwie zusammenhängen.

Weiß jemand por qué await ist innerhalb des Körpers einer Lock-Anweisung nicht erlaubt?

43 Stimmen

Ich könnte mir vorstellen, dass Sie den Grund gefunden haben, warum das nicht erlaubt ist.

3 Stimmen

Darf ich diesen Link empfehlen? hanselman.com/blog/ und diese hier: blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx

0 Stimmen

Ich fange gerade an, aufzuholen und lernen ein wenig mehr über async Programmierung. Nach zahlreichen Deadlocks in meinem wpf-Anwendungen, fand ich diesen Artikel zu einem großen sicheren Schutz in async Programmierung Praktiken sein. msdn.microsoft.com/de-us/magazine/

1voto

Anton Pogonets Punkte 1134

Hmm, sieht hässlich aus, scheint zu funktionieren.

static class Async
{
    public static Task<IDisposable> Lock(object obj)
    {
        return TaskEx.Run(() =>
            {
                var resetEvent = ResetEventFor(obj);

                resetEvent.WaitOne();
                resetEvent.Reset();

                return new ExitDisposable(obj) as IDisposable;
            });
    }

    private static readonly IDictionary<object, WeakReference> ResetEventMap =
        new Dictionary<object, WeakReference>();

    private static ManualResetEvent ResetEventFor(object @lock)
    {
        if (!ResetEventMap.ContainsKey(@lock) ||
            !ResetEventMap[@lock].IsAlive)
        {
            ResetEventMap[@lock] =
                new WeakReference(new ManualResetEvent(true));
        }

        return ResetEventMap[@lock].Target as ManualResetEvent;
    }

    private static void CleanUp()
    {
        ResetEventMap.Where(kv => !kv.Value.IsAlive)
                     .ToList()
                     .ForEach(kv => ResetEventMap.Remove(kv));
    }

    private class ExitDisposable : IDisposable
    {
        private readonly object _lock;

        public ExitDisposable(object @lock)
        {
            _lock = @lock;
        }

        public void Dispose()
        {
            ResetEventFor(_lock).Set();
        }

        ~ExitDisposable()
        {
            CleanUp();
        }
    }
}

-1voto

andrew pate Punkte 3251

Ich habe versucht, mit einem Monitor (Code unten), die zu arbeiten scheint, aber hat ein GOTCHA... wenn Sie mehrere Threads haben es gibt... System.Threading.SynchronizationLockException Objektsynchronisierungsmethode wurde von einem unsynchronisierten Codeblock aufgerufen.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyNamespace
{
    public class ThreadsafeFooModifier : 
    {
        private readonly object _lockObject;

        public async Task<FooResponse> ModifyFooAsync()
        {
            FooResponse result;
            Monitor.Enter(_lockObject);
            try
            {
                result = await SomeFunctionToModifyFooAsync();
            }
            finally
            {
                Monitor.Exit(_lockObject);
            }
            return result;
        }
    }
}

Zuvor war ich einfach tun dies, aber es war in einem ASP.NET-Controller, so dass es in einem Deadlock geführt.

public async Task<FooResponse> ModifyFooAsync() { lock(lockObject) { return SomeFunctionToModifyFooAsync.Result; } }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X