我StackExchange.Redis
在我的 Nancy 应用程序中使用(SE.R)。ConnectionMultiplexer
Nancy 通过构造函数参数自动传递了一个全局变量TinyIoC
,并且任何时候我尝试使用GetDatabase
其中一种*Async
方法(同步方法仅在尝试了其中一种异步方法后才开始失败)我的应用程序死锁。
查看我的并行堆栈,我似乎有四个线程:
- 调用
Result
我的一个使用 SE.R 的任务的线程。(堆栈上有很多 Nancy 的东西,然后调用我使用 SE.R 的库,然后调用Result
. 堆栈的顶部是Monitor.Wait
)。 - 产生另外两个线程的线程。我认为这是由 SE.R. 管理的。以
Native to Managed Transition
,开头ThreadHelper.ThreadStart
,栈顶是ThreadHelper.ThreadStart_Context
. - 像这样卡住的小堆栈:
Monitor.Wait
Monitor.Wait
SocketManager.WriteAllQueues
SocketManager.cctor.AnonymousMethod__16
- 另一个看起来像这样的小堆栈:
Managed to Native Transition
SocketManager.ReadImpl
SocketManager.Read
SocketManager.cctor.AnonymousMethod__19
我几乎可以肯定这是某种僵局。我什至认为这可能与这个问题有关。但我不知道该怎么办。
使用以下代码ConnectionMultiplexer
在 Nancy 中设置:IRegistrations
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
是由在其构造函数参数列表中请求它的所有代码共享的实例。
我有一堂课叫SchemaCache
. 其中一小部分(包括引发相关错误的代码)如下:
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();
}
});
}
我还有一个类在需要缓存中的一些信息的方法中使用它:
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);
}
以上所有方法在 LinqPad 中都可以正常工作,其中所有问题都只有一个实例。相反,它失败了TimeoutException
(后来出现了关于没有可用连接的异常)。唯一的区别是我通过依赖注入获得了一个缓存实例,而且我很确定 Nancy 使用 Tasks 来并行化请求。