我想知道这种情况是否是线程安全的,是否存在我目前没有看到的问题:
从ASP.net控制器中我从非静态类调用非静态方法(此类在另一个项目中,类被注入到控制器中)。
这个方法(非静态的)做了一些工作并调用了一些其他静态方法传递给它userId
最后,静态方法可以完成一些工作(需要userId)
我相信这种方法是线程安全的,并且如果两个用户同时调用此方法,那么一切都将正确完成(让我们说同样的纳秒)。我是正确还是完全错误?如果我错了,在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));
}
}
答案 0 :(得分:2)
注意:这个答案是在OP修改他们的问题之前发布的,以添加他们的代码,这表明这实际上是async / await是否是线程安全的问题。
静态方法本身并不是问题。如果静态方法是自包含的并且仅使用局部变量设法完成其工作,那么它是完全线程安全的。
如果静态方法不是自包含的(委托给线程不安全的代码),或者它以非线程安全的方式操作静态状态,即访问静态变量以进行读取和写入,则会出现问题。 lock()
条款。
例如,int.parse()
和int.tryParse()
是静态的,但完全是线程安全的。想象一下如果它们不是线程安全的恐怖。
答案 1 :(得分:2)
&#34;线程安全&#34;不是一个独立的术语。线程安全在面对什么?你期待什么样的并发修改?
让我们来看几个方面:
DatabaseContext
。这看起来像一个sql数据库,那些往往是线程&#34;安全&#34;,但究竟是什么意思取决于所讨论的数据库。例如,您要删除Trenings
行,如果某个其他线程也删除了同一行,您可能会遇到(安全)并发冲突异常。并且根据隔离级别,即使对于&#34; Trenings&#34;的其他特定突变,可能也会获得并发冲突异常。在最坏的情况下,这意味着一个失败的请求,但数据库本身不会被破坏。但是,您的代码似乎过于复杂。如果您希望能够长期保持这一点,我建议您大大简化它。
ref
参数,除非您真的知道自己在做什么(这里没有必要)。 ToString()
。 绝对避免像Contains
那样令人讨厌的技巧来检查密钥相等性。你希望你的代码在发生意外事件时中断,因为代码会一直li&#34;实际上几乎不可能调试(你将写错误)。答案 2 :(得分:1)
你在这里做的是在列表上同步(deleteTasks)。如果你这样做我会推荐2件事中的一件。
1)使用线程安全集合 https://msdn.microsoft.com/en-us/library/dd997305(v=vs.110).aspx
2)让您的DeleteItemFromRedisAsync返回一个任务并等待它。
虽然我认为在这种特殊情况下,一旦你重构它并没有看到任何问题,并且可以多次并行调用DeleteItemFromRedisAsync,那么你将遇到问题。原因是如果多个线程可以修改你的deleteTasks列表,那么你不再保证你全部收集它们(https://msdn.microsoft.com/en-us/library/dd997373(v=vs.110).aspx如果2个线程做了一个“添加”/添加到非线程安全的方式同时然后其中一个丢失了)所以你可能在等待所有这些任务完成时错过了一个任务。
另外,我会避免混合范式。使用async / await或跟踪任务集合并让方法添加到该列表。不要两者都做。从长远来看,这将有助于代码的可维护性。 (注意,线程仍然可以返回一个任务,你收集它们然后等待它们全部。但是收集方法负责任何线程问题,而不是隐藏在被调用的方法中)