[英]Calling static async methods from ASP.net project
我想知道這個場景是否是線程安全的,是否存在我目前沒有看到的問題:
從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));
}
}
注意:這個答案是在OP修改他們的問題之前發布的,以添加他們的代碼,這表明這實際上是async / await是否是線程安全的問題。
靜態方法本身不是問題。 如果靜態方法是自包含的並且僅使用局部變量設法完成其工作,那么它是完全線程安全的。
如果靜態方法不是自包含的(委托給線程不安全的代碼),或者它以非線程安全的方式操作靜態狀態,即在lock()
外部讀取和寫入靜態變量,則會出現問題條款。
例如, int.parse()
和int.tryParse()
是靜態的,但完全是線程安全的。 想象一下如果他們不是線程安全的恐怖。
“線程安全”不是一個獨立的術語。 線程安全在面對什么? 你期待什么樣的並發修改?
我們來看幾個方面:
DatabaseContext
。 這看起來像一個sql數據庫,那些往往是線程“安全”,但究竟是什么意思取決於所討論的數據庫。 例如,您正在刪除Trenings
行,如果某個其他線程也刪除了同一行,您可能會遇到(安全)並發沖突異常。 並且根據隔離級別,即使對於“Trenings”的其他特定突變,您也可能會遇到並發沖突異常。 在最壞的情況下,這意味着一個失敗的請求,但數據庫本身不會損壞。 但是,您的代碼似乎過於復雜。 如果您希望能夠長期保持這一點,我建議您大大簡化它。
ref
參數(這里沒有必要)。 ToString()
。 絕對避免像Contains
這樣令人討厭的技巧來檢查密鑰相等。 您希望代碼在發生意外情況時中斷,因為“跛行”的代碼幾乎無法調試(並且您將編寫錯誤)。 你在這里做的是在列表上同步(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個線程同時以非線程安全的方式執行“添加”/“添加到結束”,則其中1個丟失)因此您可能在等待所有任務完成時錯過了任務。
我也會避免混合范式。 使用async / await或跟蹤任務集合並讓方法添加到該列表。 不要兩者都做。 從長遠來看,這將有助於代碼的可維護性。 (注意,線程仍然可以返回一個任務,你收集它們然后等待它們全部。但是收集方法負責任何線程問題,而不是隱藏在被調用的方法中)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.