我想知道这种情况是否是线程安全的,是否存在我目前未发现的问题:
我从 ASP.net Controller 调用非静态类的非静态方法(这个类在另一个项目中,类被注入(inject)到 Controller 中)。
这个方法(非静态的)做一些工作并调用一些其他静态方法传递给它 userId
最后静态方法做了一些工作(为此需要 userId)
我相信这种方法是线程安全的,如果两个用户同时调用此方法(假设在同一纳秒内),一切都会正确完成。我是正确的还是完全错误的?如果我错了,在 ASP.net 项目中使用静态方法的正确方法是什么?
编辑
这是代码:)
这是来自 Controller 的调用:
await _workoutService.DeleteWorkoutByIdAsync(AzureRedisFeedsConnectionMultiplexer.GetRedisDatabase(),AzureRedisLeaderBoardConnectionMultiplexer.GetRedisDatabase(), workout.Id, userId);
这里是 DeleteWorkoutByIdAsync 的样子:
public async Task<bool> DeleteWorkoutByIdAsync(IDatabase redisDb,IDatabase redisLeaderBoardDb, Guid id, string userId)
{
using (var databaseContext = new DatabaseContext())
{
var workout = await databaseContext.Trenings.FindAsync(id);
if (workout == null)
{
return false;
}
databaseContext.Trenings.Remove(workout);
await databaseContext.SaveChangesAsync();
await RedisFeedService.StaticDeleteFeedItemFromFeedsAsync(redisDb,redisLeaderBoardDb, userId, workout.TreningId.ToString());
}
return true;
}
如您所见,DeleteWorkoutByIdAsync 调用静态方法 StaticDeleteFeedItemFromFeedsAsync,如下所示:
public static async Task StaticDeleteFeedItemFromFeedsAsync(IDatabase redisDb,IDatabase redisLeaderBoardDd, string userId, string workoutId)
{
var deleteTasks = new List<Task>();
var feedAllRedisVals = await redisDb.ListRangeAsync("FeedAllWorkouts:" + userId);
DeleteItemFromRedisAsync(redisDb, feedAllRedisVals, "FeedAllWorkouts:" + userId, workoutId, ref deleteTasks);
await Task.WhenAll(deleteTasks);
}
这是在 StaticDeleteFeedItemFromFeedsAsync 中调用的静态方法 DeleteItemFromRedisAsync:
private static void DeleteItemFromRedisAsync(IDatabase redisDb, RedisValue [] feed, string redisKey, string workoutId, ref List<Task> deleteTasks)
{
var itemToRemove = "";
foreach (var f in feed)
{
if (f.ToString().Contains(workoutId))
{
itemToRemove = f;
break;
}
}
if (!string.IsNullOrEmpty(itemToRemove))
{
deleteTasks.Add(redisDb.ListRemoveAsync(redisKey, itemToRemove));
}
}
最佳答案
“线程安全”不是一个独立的术语。线程安全在面对什么?您希望此处进行什么样的并发修改?
让我们从几个方面来看:
- 您自己的可变共享状态:您在此代码中没有任何共享状态;所以它自动是线程安全的。
- 间接共享状态:
DatabaseContext
。这看起来像一个 sql 数据库,而且它们往往是线程“安全的”,但这究竟意味着什么取决于所讨论的数据库。例如,您要删除Trenings
行,如果其他线程也删除了同一行,您可能会遇到(安全的)并发冲突异常。根据隔离级别,您可能获得并发冲突异常,即使是“Trenings”的其他某些突变也是如此。在最坏的情况下,这意味着一个请求失败,但数据库本身不会损坏。 - Redis 本质上是单线程的,因此所有操作都是序列化的,从这个意义上说是“线程安全的”(这可能不会给您带来太多好处)。您的删除代码获取一组键,然后最多删除其中一个。如果两个或多个线程同时尝试删除同一个键,一个线程可能会尝试删除一个不存在的键,这可能出乎您的意料(但不会导致数据库损坏)。
- redis+sql 之间的隐式一致性:看起来你正在使用 guid,因此不相关的东西发生冲突的可能性很小。您的示例仅包含删除操作(很可能不会导致一致性问题),因此很难推测在所有其他 情况下,redis 和 sql 数据库是否会保持一致。一般来说,如果您的 ID 从未被重复使用,您可能是安全的 - 但保持两个数据库同步是一个难题,而且您很可能会在某个地方犯错。
但是,您的代码对于它正在做的事情来说似乎过于复杂。如果您希望能够长期维护它,我建议您大幅简化它。
- 不要使用
ref
参数,除非你真的知道你在做什么(这里没有必要)。 - 不要将字符串与其他数据类型混淆,因此请尽可能避免使用
ToString()
。 绝对避免使用Contains
等讨厌的技巧来检查键是否相等。您希望您的代码在意外发生时中断,因为“一瘸一拐”的代码几乎不可能调试(而且您会编写错误)。 - 如果您真正能做的唯一一件事就是等待所有任务,则不要有效地返回一组任务 - 不妨在被调用方中这样做以简化 API。
- 不要使用 redis。这可能只是一种干扰——你已经有了另一个数据库,所以你不太可能在这里需要它,除非出于性能原因,而且现在为假设的性能问题添加整个额外的数据库引擎还为时过早。有一种合理的可能性是,需要额外连接的额外开销可能会使您的代码比只有一个数据库时慢,尤其是当您无法保存许多 sql 查询时。
关于c# - 从 ASP.net 项目调用静态异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34070160/