Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I keep my Async method thread safe?

I need to write a method in my Windows Universal App to write to the SD card. How can I ensure two threads do not try to write to the same file at the same time in my method below ?

public async void WriteToCard(string strFileName, IEnumerable<string> listLinesToWrite)
{
    IStorageItem item = await folder.GetItemAsync(strFileName);
    StorageFile file = (StorageFile)item;

    await Windows.Storage.FileIO.WriteLinesAsync(file, listLinesToWrite);
}
like image 316
Bill Greer Avatar asked Feb 09 '23 06:02

Bill Greer


2 Answers

You could keep a map with a ConcurrentDictionary which maps each file to a SemaphoreSlim. Then, fetch each semaphore based on the file location you'll be writing to:

private ConcurrentDictionary<string, SemaphoreSlim> fileLocks = new ConcurrentDictionary<string, SemaphoreSlim>();

public async Task WriteToCardAsync(string strFileName, IEnumerable<string> listLinesToWrite)
{
   var semaphoreSlim = fileLocks.GetOrAdd(strFileName, new SemaphoreSlim(1, 1));

   await semaphoreSlim.WaitAsync();
   try
   {
       IStorageItem item = await folder.GetItemAsync(strFileName);
       StorageFile file = (StorageFile)item;

       await Windows.Storage.FileIO.WriteLinesAsync(file, listLinesToWrite);
   }
   finally
   {
       semaphoreSlim.Release();
   }
}

Side note - Use async Task instead of async void. I also added the Async postfix to the methods.

like image 122
Yuval Itzchakov Avatar answered Feb 12 '23 02:02

Yuval Itzchakov


You can use the AsyncEx library, which contains an AsyncLock

Assuming there won't be a huge number of filenames, you can use a ConcurrentDictionary to associate a filename to a lock - otherwise, the dictionary size might grow unbound.

private readonly locks = new ConcurrentDictionary<string, AsyncLock>();

public async void WriteToCard(string strFileName, IEnumerable<string> listLinesToWrite)
{
    var lock = locks.GetOrAdd(strFileName, () => new AsyncLock());

    using (await lock.LockAsync())
    {
        IStorageItem item = await folder.GetItemAsync(strFileName);
        StorageFile file = (StorageFile)item;

        await Windows.Storage.FileIO.WriteLinesAsync(file, listLinesToWrite);
    }
}
like image 24
dcastro Avatar answered Feb 12 '23 03:02

dcastro