2014-12-01 12 views
5

Nancy uygulamamda StackExchange.Redis (SE.R henceforth) kullanıyorum. Yapıcı parametreleri üzerinden Nancy'nin TinyIoC tarafından otomatik olarak geçirilen ve GetDatabase ve *Async yöntemlerinden birini kullanıp kullanmaya çalıştığım tek bir global ConnectionMultiplexer var (eşitleme yöntemlerinden biri denendikten sonra eşitleme yöntemleri yalnızca başarısız oluyor) kilitlenmeler. O SE.R. kullanan benim Görevler birinde Result denilenStackExchange.Redis Deadlocking

  1. iplik:

    benim paralel yığınlar baktığımızda dört konuları olduğu görünüyor (Yığında çok sayıda Nancy parçası var, daha sonra SE.R kullanan kütüphanemle bir çağrı ve Result numaralı telefonu arayın. Yığının üst kısmı Monitor.Wait).

  2. Diğer iki iş parçacığını oluşturan bir iş parçacığı. Bunun SE.R. tarafından yönetildiğini düşünüyorum. Native to Managed Transition, ThreadHelper.ThreadStart ile başlar ve yığının en üstünde ThreadHelper.ThreadStart_Context bulunur. Böyle kalmış
  3. küçük bir deste
    • Monitor.Wait
    • Monitor.Wait
    • SocketManager.WriteAllQueues
    • SocketManager.cctor.AnonymousMethod__16
  4. şöyle başka küçük bir yığın:
    • SocketManager.cctor.AnonymousMethod__19

  • SocketManager.ReadManaged to Native Transition
  • Bu çeşit bir kilitlenme neredeyse eminim. Hatta bunun this question ile ilgisi olabileceğini düşünüyorum. Ama bunun hakkında ne yapacağımı bilmiyorum.

    ConnectionMultiplexer

    aşağıdaki kodla bir Nancy IRegistrations yılında kurulduğundan:

    var configOpts = new ConfigurationOptions { 
        EndPoints = { 
        RedisHost, 
        }, 
        Password = RedisPass, 
        AllowAdmin = false, 
        ClientName = ApplicationName, 
        ConnectTimeout = 10000, 
        SyncTimeout = 5000, 
    }; 
    var mux = ConnectionMultiplexer.Connect(configOpts); 
    yield return new InstanceRegistration(typeof (ConnectionMultiplexer), mux); 
    

    mux onların yapıcı parametre listesinde istemeyi tüm kod tarafından paylaşılacağını örneğidir.

    SchemaCache adlı bir dersim var. Bunun küçük bir parça (yani söz konusu hatayı atar kodu içerir) aşağıdaki gibidir:

    public SchemaCache(ConnectionMultiplexer connectionMultiplexer) { 
        ConnectionMultiplexer = connectionMultiplexer; 
    } 
    
    private ConnectionMultiplexer ConnectionMultiplexer { get; set; } 
    
    private async Task<string[]> Cached(string key, bool forceFetch, Func<string[]> fetch) { 
        var db = ConnectionMultiplexer.GetDatabase(); 
    
        return forceFetch || !await db.KeyExistsAsync(key) 
         ? await CacheSetSet(db, key, await Task.Run(fetch)) 
         : await CacheGetSet(db, key); 
    } 
    
    private static async Task<string[]> CacheSetSet(IDatabaseAsync db, string key, string[] values) { 
        await db.KeyDeleteAsync(key); 
        await db.SetAddAsync(key, EmptyCacheSentinel); 
    
        var keysSaved = values 
         .Append(EmptyCacheSentinel) 
         .Select(val => db.SetAddAsync(key, val)) 
         .ToArray() 
         .Append(db.KeyExpireAsync(key, TimeSpan.FromDays(1))); 
        await Task.WhenAll(keysSaved); 
    
        return values; 
    } 
    
    private static async Task<string[]> CacheGetSet(IDatabaseAsync db, string key) { 
        var results = await db.SetMembersAsync(key); 
        return results.Select(rv => (string) rv).Without(EmptyCacheSentinel).ToArray(); 
    } 
    
    // There are a bunch of these public methods: 
    public async Task<IEnumerable<string>> UseCache1(bool forceFetch = false) { 
        return await Cached("the_key_i_want", forceFetch,() => { 
         using (var cnn = MakeConnectionToDatabase("server", "databaseName")) { 
          // Uses Dapper: 
          return cnn.Query<string>("--expensive sql query").ToArray(); 
         } 
        }); 
    } 
    

    Ben de önbellekten bazı bilgileri gerektiren bir yöntemde bu kullanımlarını yapan bir sınıf var:

    public OtherClass(SchemaCache cache) { 
        Cache = cache; 
    } 
    
    private SchemaCache Cache { get; set; } 
    
    public Result GetResult(Parameter parameter) { 
        return Cache.UseCache1().Result 
         .Where(r => Cache.UseCache2(r).Result.Contains(parameter)) 
         .Select(r => CheckResult(r)) 
         .FirstOrDefault(x => x != null); 
    } 
    

    Yukarıdaki çalışmaların tümü, söz konusu her şeyin yalnızca bir örneğinin bulunduğu LinqPad'de sorun değil. Bunun yerine, bir TimeoutException (ve daha sonra kullanılabilir bağlantı olmamasıyla ilgili bir istisna) ile başarısız olur.Tek fark, bağımlılık enjeksiyonu yoluyla önbellek örneğini almamdır ve eminim ki Nancy istekleri paralel hale getirmek için Görevler'i kullanıyor. İşte

  • +0

    Bize kodunuzu gösterin. –

    +0

    Yine de özetlemeye çalışıyorum. – Crisfole

    +0

    Bir işlem veya toplu iş kullanma şansınız var mı? Kendinizi orada kilitlemek için gerçekten basit bir yol var (bu kadar kolay bir şekilde sabittir) –

    cevap

    10

    biz gitmek:

    return Cache.UseCache1().Result 
    

    patlaması; kilitlenmeye. Ama StackExchange.Redis ile ilgisi yok.

    En azından, çoğu senkronizasyon bağlamı sağlayıcılarından. Bunun nedeni, await s'nizin örtük olarak sync-context aktivasyonunu talep etmeleridir - bu, "UI thread" (winforms, WPF) veya "şu anda atanmış worker thread" (WCF, ASP.NET, MVC, vb.) Anlamına gelebilir. . Buradaki sorun, 'un bu iş parçacığı hiçbir zaman öğelerini işlemek için kullanılamayacağıdır, çünkü .Result bir zamanlayıcı ve engelleme çağrısıdır. Bu nedenle, tamamlama geri aramalarınızın hiçbiri işlenmeyecektir, çünkü bunları işleyebilecek tek iş parçacığı, kendisini kullanılabilir hale getirmeden önce bitirmelerini beklemektedir.

    Not: StackExchange.Redis , eşitleme bağlamını kullanmaz; deadlock'ların nedeni olmaktan kaçınmak için senkronizasyon bağlamından açıkça ayrılır (bu normaldir ve kütüphaneler için önerilir). Anahtar nokta, kodunuzun olmamasıdır.

    Seçenekler:

    • sizin await çağrıların hepsi (veya en azından bu .UseCache1() altına) açıkça .ConfigureAwait(false) aramasını async ve/.Wait().Result veya
    • karışmaz - Ancak not bu o arama bağlamında tamamlanmaların gerçekleşmeyeceği anlamına gelir!

    İlk seçenek en basit olanıdır; senkronizasyon bağlamına bağlı olmayan çağrıların bir ağacını ayırabiliyorsanız, ikinci yaklaşım geçerli olur.

    Bu çok yaygın bir sorundur; I did pretty much the same thing.

    +0

    Siz sallanıyorsunuz. Ben de aynı şeyi yaptığına sevindim. Bunu çok iyi açıkladığın için çok teşekkürler. – Crisfole

    +0

    Yığım temelde: NancyFx + Marc Gravell ... bu yüzden kaynaklardan yardım almak her zaman güzel;) – Crisfole