2 votes

Verrouillage en fonction de l'Id dans une méthode asynchrone

J'ai une application MVC dans laquelle les méthodes d'action doivent être exécutées dans un certain ordre. Récemment, j'ai eu quelques problèmes étranges et j'ai supposer que c'est dû au fait que j'ai ne pas faire de synchronisation des threads . J'ai à peine travaillé avec le multithreading et je ne connais pas grand-chose à ce sujet. J'ai essayé d'implémenter une sorte de verrouillage où je dois verrouiller selon Id. J'ai donc implémenté la classe ci-dessous pour obtenir les objets de verrouillage requis.

public class ReportLockProvider
    : IReportLockProvider
{
    readonly ConcurrentDictionary<long, object> lockDictionary 
        = new ConcurrentDictionary<long, object>();

    public object ProvideLockObject(long reportId)
    {
        return lockDictionary.GetOrAdd(reportId, new object());
    }
}

J'ai essayé de l'utiliser comme suit :

ReportLockProvider lockProvider = new ReportLockProvider();

public async ActionResult MyAction(long reportId)
{
    lock(lockProvider.ProvideLockObject(reportId))
    {
        // Some operations
        await Something();
        // Some operation
    }
}

J'espérais que cela fonctionnerait, mais cela n'a pas été le cas, car j'ai utilisé await à l'intérieur lock corps. J'ai cherché un peu et je suis tombé sur SemaphoreSlim en cette réponse . Le problème est que je dois obtenir l'objet de verrouillage selon Id . Comment faire ? Est-il possible de créer plusieurs SemaphoreSlim objets ? Puis-je modifier le code comme suit :

public class ReportLockProvider
    : IReportLockProvider
{
    readonly ConcurrentDictionary<long, SemaphoreSlim> lockDictionary 
        = new ConcurrentDictionary<long, SemaphoreSlim>();

    public SemaphoreSlim ProvideLockObject(long reportId)
    {
        return lockDictionary.GetOrAdd(reportId, new SemaphoreSlim(1, 1));
    }
}

public async ActionResult MyAction(long reportId)
{
    var lockObject = ReportLockProvider.ProvideLockObject(reportId);

    await lockObject.WaitAsync();
    try
    {
        // Some operations
        await Something();
        // Some operation
    }
    finally
    {
        lockObject.Release();
    }
}

L'autre question est la suivante : puis-je utiliser SemaphoreSlim en non synchrone méthodes ? Existe-t-il une meilleure option ?

0voto

Thomas Points 194

Je pense qu'il manque un mot-clé statique devant votre lockDictionary, mais cela dépend de la façon dont vous instanciez le fournisseur.

Voici un exemple avec un petit code de modification que j'ai préparé dans LinqPad :

async Task Main()
{
    ReportLockProvider reportLockProvider = new ReportLockProvider();
    List<Task> tasks = new List<Task>(10);

    for (long i = 1; i <= 5; i++) {
        var local = i;
        tasks.Add(Task.Run(() => Enter(local) ));
        tasks.Add(Task.Run(() => Enter(local) ));
    }

    async Task Enter(long id)
    {
        Console.WriteLine(id + " waiting to enter");
        await reportLockProvider.WaitAsync(id);

        Console.WriteLine(id + " entered!");
        Thread.Sleep(1000 * (int)id);

        Console.WriteLine(id + " releasing");
        reportLockProvider.Release(id);
    }

    await Task.WhenAll(tasks.ToArray());
}

public class ReportLockProvider
{
    static readonly ConcurrentDictionary<long, SemaphoreSlim> lockDictionary = new ConcurrentDictionary<long, SemaphoreSlim>();

    public async Task WaitAsync(long reportId)
    {
        await lockDictionary.GetOrAdd(reportId, new SemaphoreSlim(1, 1)).WaitAsync();
    }
    public void Release(long reportId)
    {
        SemaphoreSlim semaphore;
        if (lockDictionary.TryGetValue(reportId, out semaphore))
        {
            semaphore.Release();
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X