在线程环境中运行时,我遇到了一些redis问题。
我有一个名为AwaitableParallelForeachWorker的类,我可以在其中为有效负载中的每个项目运行特定的函数。 (我知道它并不漂亮,但它能完成这项工作)
public class AwaitableParallelForeachWorker : IAwaitableParallelForeachWorker
{
private readonly object _lockObject = new object();
private int _tasksCompleted;
public async Task Run<T>(Func<T, Task> action, IEnumerable<T> payload)
{
var list = payload.ToList();
var tasks = list.Select(x => new Task(async () =>
{
await action(x);
TaskDone();
}));
Parallel.ForEach(tasks, task => task.Start());
while (_tasksCompleted < list.Count)
{
await Task.Delay(10);
}
}
public void TaskDone()
{
lock (_lockObject)
{
_tasksCompleted++;
}
}
}
这里是redis缓存代码:
public class NewsappRedisCache : INewsappRedisCache
{
private static readonly string ConnectionString = ConfigurationManager.AppSettings["RedisCache"];
private static readonly Lazy<ConnectionMultiplexer> LazyConnection =
new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(ConnectionString));
private static IDatabase MimerArticleDatabase => Connection.GetDatabase(2);
public async Task<MimerArticle> GetMimerArticleAsync(Guid id)
{
var redisValue = await MimerArticleDatabase.StringGetAsync($"{nameof(MimerArticle)}-{id}");
if (!redisValue.HasValue) return null;
var mimerArticle = JsonConvert.DeserializeObject<MimerArticle>(redisValue.ToString());
return mimerArticle;
}
我写过这个简单的测试,使用AwaitableParallelForeachWorker调用我的redis缓存1000次
public class Test
{
private NewsappRedisCache _redisCache;
[Fact]
public async void TestRedis()
{
var guids = new List<Guid>();
for (var i = 0; i < 1000; i++)
{
guids.Add(Guid.NewGuid());
}
_redisCache = new NewsappRedisCache();
await new AwaitableParallelForeachWorker().Run(CallRedis, guids);
}
private async Task CallRedis(Guid id)
{
await _redisCache.GetMimerArticleAsync(id);
}
}
现在变得奇怪了。有时1000秒到达redis缓存会在瞬间执行。我已经通过检查azure门户网站验证了实际上达到了缓存。但有时每次获得大约需要1秒钟。
我不知道为什么。我曾尝试更改AwaitableParallelForeachWorker的功能,但它仍然不一致。 如果我运行每个get in a normal foreach它执行得很好,但没有AwaitableParallelForeachWorker实际工作时那么快。
所以我觉得它与线程/任务有关。
任何可以提供帮助的人?
答案 0 :(得分:1)
我找不到确切的原因,但我也不能在评论中说明这一点,所以这里有一些事情需要考虑:
测试方法不应该是async Task
,而不是async void
吗?一些测试框架处理这个可以说是不正确的用法,但也许你的用法没有。如果框架无法知道方法何时完成,那么它会认为一切都是在第一次完成时完成的。
您的Run()
方法可以大大简化为:
public Task Run<T>(Func<T, Task> action, IEnumerable<T> payload)
{
return Task.WhenAll(payload.Select(action));
}
并摆脱课堂上的其他人。
您的CallRedis()
方法不需要状态机开销;代替:
private Task CallRedis(Guid id)
{
return _redisCache.GetMimerArticleAsync(id);
}
虽然我在为你节省行数:
var guids = Enumerable.Range(0, 1000).Select(Guid.NewGuid()).ToList();
// :)