我想知道这个场景是否是线程安全的,是否存在我目前没有看到的问题:
我相信这种方法是线程安全的,如果两个用户同时调用此方法(假设在相同的纳秒内),那么所有的操作都会正确地完成。我是对的还是完全错的?如果我错了,在ASP.net项目中使用静态方法的正确方法是什么?
编辑
这是代码:)
这是来自控制器的调用:
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);
}下面是静态方法DeleteItemFromRedisAsync,它在StaticDeleteFeedItemFromFeedsAsync中被调用:
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));
}
}发布于 2015-12-12 14:03:36
“线程安全”不是一个独立的术语。螺纹安全面对什么?您期望在这里进行什么样的并行修改?
让我们在这里看几个方面:
DatabaseContext。这看起来像一个sql数据库,而这些数据库往往是线程“安全”的,但这到底意味着什么取决于所讨论的数据库。例如,您正在删除一个Trenings行,如果其他线程也删除了同一行,则可能会得到一个(安全的)并发冲突异常。根据隔离级别的不同,即使在"Trenings“的其他特定突变中,也可能会出现并发冲突异常。在最坏的情况下,这意味着一个失败的请求,但数据库本身不会损坏。但是,您的代码对于它所做的工作似乎过于复杂。如果您希望能够长期保持这种状态,我建议您大大简化它。
ref参数,除非您真正知道自己在做什么(这里没有必要)。ToString()。当然,要避免像Contains这样的讨厌的技巧来检查密钥是否相等。当一些意想不到的事情发生时,您希望您的代码中断,因为“蹒跚而行”的代码实际上是不可能调试的(您将编写bug)。发布于 2015-12-03 15:54:24
注意:这个答案是在OP修改他们的问题以添加他们的代码之前发布的,这表明这实际上是一个异步/等待是否是线程安全的问题。
静态方法本身并不是一个问题。如果静态方法是自包含的,并且只能使用局部变量来完成它的工作,那么它是完全线程安全的。
如果静态方法不是自包含的(委托给线程不安全的代码),或者它以非线程安全的方式操作静态状态,即在lock()子句之外访问静态变量,就会出现问题。
例如,int.parse()和int.tryParse()是静态的,但是完全线程安全。想象一下,如果它们不是线程安全的话,会有多可怕。
发布于 2015-12-09 09:17:15
您在这里所做的是对列表(deleteTasks)进行同步。如果你这样做,我会推荐两件事中的一件。
1)要么使用线程安全集合https://msdn.microsoft.com/en-us/library/dd997305(v=vs.110).aspx
2)让DeleteItemFromRedisAsync返回一个任务并等待它。
虽然我认为在这个特殊的情况下,我没有看到任何问题,只要您重构它和DeleteItemFromRedisAsync可以被多次并行调用,那么您就会有问题。原因是,如果多个线程可以修改您的deleteTasks列表,那么您就不能保证您收集它们全部(如果两个线程同时以非线程安全的方式执行“Add”/Add end,则其中一个线程丢失了),所以当等待它们全部完成时,您可能已经错过了一个任务。
此外,我也会避免混合范式。要么使用异步/等待,要么跟踪一组任务,并让方法添加到该列表中。两样都别做。从长远来看,这将有助于提高代码的可维护性。(注意,线程仍然可以返回一个任务,您收集这些任务,然后等待所有这些任务。但是,收集方法负责任何线程处理问题,而不是隐藏在被调用的方法中)
https://stackoverflow.com/questions/34070160
复制相似问题