相关疑难解决方法(0)

基于密钥的异步锁定

我试图弄清楚,已经提出了与我的ImageProcessor库的问题在这里添加项目到缓存中,当在那里我得到间歇性文件访问错误.

System.IO.IOException:进程无法访问文件'D:\ home\site\wwwroot\app_data\cache\0\6\5\f\2\7\065f27fc2c8e843443d210a1e84d1ea28bbab6c4.webp',因为它正被另一个进程使用.

我编写了一个类,用于基于散列网址生成的密钥执行异步锁定,但似乎我在实现中遗漏了一些东西.

我的锁类

public sealed class AsyncDuplicateLock
{
    /// <summary>
    /// The collection of semaphore slims.
    /// </summary>
    private static readonly ConcurrentDictionary<object, SemaphoreSlim> SemaphoreSlims
                            = new ConcurrentDictionary<object, SemaphoreSlim>();

    /// <summary>
    /// Locks against the given key.
    /// </summary>
    /// <param name="key">
    /// The key that identifies the current object.
    /// </param>
    /// <returns>
    /// The disposable <see cref="Task"/>.
    /// </returns>
    public IDisposable Lock(object key)
    {
        DisposableScope releaser = new DisposableScope(
        key,
        s =>
        {
            SemaphoreSlim locker; …
Run Code Online (Sandbox Code Playgroud)

c# multithreading locking async-await imageprocessor

17
推荐指数
2
解决办法
4809
查看次数

Async threadsafe从MemoryCache获取

我创建了一个MemoryCache在下面使用.NET的异步缓存.这是代码

public async Task<T> GetAsync(string key, Func<Task<T>> populator, TimeSpan expire, object parameters)
{
    if(parameters != null)
        key += JsonConvert.SerializeObject(parameters);

    if(!_cache.Contains(key))
    {
        var data = await populator();
        lock(_cache)
        {
            if(!_cache.Contains(key)) //Check again but locked this time
                _cache.Add(key, data, DateTimeOffset.Now.Add(expire));
        }
    }

    return (T)_cache.Get(key);
}
Run Code Online (Sandbox Code Playgroud)

我认为唯一的缺点是我需要在锁外等待,所以populator不是线程安全的,但是因为await不能驻留在锁内,我想这是最好的方法.我错过了任何陷阱吗?

更新:当antoher线程使缓存无效时,Esers应答的版本也是线程安全的

public async Task<T> GetAsync(string key, Func<Task<T>> populator, TimeSpan expire, object parameters)
{
    if(parameters != null)
        key += JsonConvert.SerializeObject(parameters);

    var lazy = new Lazy<Task<T>>(populator, true);
    _cache.AddOrGetExisting(key, lazy, DateTimeOffset.Now.Add(expire));
    return ((Lazy<Task<T>>) _cache.Get(key)).Value;
}
Run Code Online (Sandbox Code Playgroud)

但它可能会更慢,因为它会创建永远不会执行的Lazy实例,并且它在完全线程安全模式下使用Lazy …

c# thread-safety async-await

16
推荐指数
4
解决办法
7317
查看次数

带有异步初始化的 Lazy&lt;Task&lt;T&gt;&gt;

class Laziness
{
    static string cmdText = null;
    static SqlConnection conn = null;

 
    Lazy<Task<Person>> person =
        new Lazy<Task<Person>>(async () =>      
        {
            using (var cmd = new SqlCommand(cmdText, conn))
            using (var reader = await cmd.ExecuteReaderAsync())
            {
                if (await reader.ReadAsync())
                {
                    string firstName = reader["first_name"].ToString();
                    string lastName = reader["last_name"].ToString();
                    return new Person(firstName, lastName);
                }
            }
            throw new Exception("Failed to fetch Person");
        });

    public async Task<Person> FetchPerson()
    {
        return await person.Value;              
    }
}
Run Code Online (Sandbox Code Playgroud)

Riccardo Terrell 于 2018 年 6 月出版的《.NET 中的并发》一书说道: …

c# asynchronous lazy-evaluation async-await

15
推荐指数
2
解决办法
2万
查看次数

System.Lazy <T>具有不同的线程安全模式

.NET 4.0的System.Lazy <T>类通过枚举LazyThreadSafetyMode提供三种线程安全模式,我将其概括为:

  • LazyThreadSafetyMode.None - 不是线程安全的.
  • LazyThreadSafetyMode.ExecutionAndPublication - 只有一个并发线程将尝试创建基础值.成功创建后,所有等待的线程将获得相同的值.如果在创建期间发生未处理的异常,则将在每个等待的线程上重新抛出它,在每次后续尝试访问基础值时进行缓存和重新抛出.
  • LazyThreadSafetyMode.PublicationOnly - 多个并发线程将尝试创建基础值,但第一个成功将确定传递给所有线程的值.如果在创建期间发生未处理的异常,则不会对其进行高速缓存,并且并发和后续尝试访问基础值将重新尝试创建并可能成功.

我想要一个延迟初始化的值,它遵循稍微不同的线程安全规则,即:

只有一个并发线程将尝试创建基础值.成功创建后,所有等待的线程将获得相同的值.如果在创建期间发生未处理的异常,它将在每个等待的线程上重新抛出,但它不会被缓存,后续尝试访问基础值将重新尝试创建并可能成功.

因此,与LazyThreadSafetyMode.ExecutionAndPublication的关键不同之在于,如果创建时"先行"失败,则可以在以后重新尝试.

是否存在提供这些语义的现有(.NET 4.0)类,还是我必须自己编写?如果我自己滚动是否有一种聪明的方法可以在实现中重用现有的Lazy <T>以避免显式锁定/同步?


注意对于一个用例,假设"创建"可能很昂贵并且容易出现间歇性错误,例如从远程服务器获取大量数据.我不想进行多次并发尝试来获取数据,因为它们可能都会失败或全部成功.但是,如果它们失败了,我希望以后能够重试.

c# .net-4.0 thread-safety

7
推荐指数
1
解决办法
2092
查看次数

ConcurrentDictionary GetOr添加异步

我想使用诸如GetOrAdda之类的东西ConcurrentDictionary作为Web服务的缓存。该词典有异步版本吗?GetOrAdd将使用发出Web请求HttpClient,因此,如果该词典的某个版本中GetOrAdd是异步的,那就太好了。

为了消除混淆,字典的内容将是对Web服务的调用的响应。

ConcurrentDictionary<string, Response> _cache = new ConcurrentDictionary<string, Response>();



var response = _cache.GetOrAdd("id", (x) => { _httpClient.GetAsync(x).GetAwaiter().GetResponse();} )
Run Code Online (Sandbox Code Playgroud)

.net c# .net-core .net-core-2.2

6
推荐指数
4
解决办法
1183
查看次数