首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从ASP.net项目调用静态异步方法

从ASP.net项目调用静态异步方法
EN

Stack Overflow用户
提问于 2015-12-03 15:43:32
回答 3查看 2.3K关注 0票数 7

我想知道这个场景是否是线程安全的,是否存在我目前没有看到的问题:

  1. 从ASP.net控制器,我从非静态类调用非静态方法(这个类在另一个项目中,类被注入控制器)。
  2. 这个方法(是非静态的)做了一些工作,并调用其他一些传递它为userId的静态方法。
  3. 最后,静态方法做了一些工作(为此需要userId )。

我相信这种方法是线程安全的,如果两个用户同时调用此方法(假设在相同的纳秒内),那么所有的操作都会正确地完成。我是对的还是完全错的?如果我错了,在ASP.net项目中使用静态方法的正确方法是什么?

编辑

这是代码:)

这是来自控制器的调用:

代码语言:javascript
复制
await _workoutService.DeleteWorkoutByIdAsync(AzureRedisFeedsConnectionMultiplexer.GetRedisDatabase(),AzureRedisLeaderBoardConnectionMultiplexer.GetRedisDatabase(), workout.Id, userId);

在这里,DeleteWorkoutByIdAsync是什么样的:

代码语言:javascript
复制
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,如下所示:

代码语言:javascript
复制
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中被调用:

代码语言:javascript
复制
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));
        }
  }
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-12-12 14:03:36

“线程安全”不是一个独立的术语。螺纹安全面对什么?您期望在这里进行什么样的并行修改?

让我们在这里看几个方面:

  • 您自己的可变共享状态:在这段代码中您没有任何共享状态;所以它自动是线程安全的。
  • 间接共享状态:DatabaseContext。这看起来像一个sql数据库,而这些数据库往往是线程“安全”的,但这到底意味着什么取决于所讨论的数据库。例如,您正在删除一个Trenings行,如果其他线程也删除了同一行,则可能会得到一个(安全的)并发冲突异常。根据隔离级别的不同,即使在"Trenings“的其他特定突变中,也可能会出现并发冲突异常。在最坏的情况下,这意味着一个失败的请求,但数据库本身不会损坏。
  • Redis本质上是单线程的,所以所有的操作都是序列化的,从这个意义上说,“线程安全”(这可能不会给您带来太多的开销)。删除代码得到一组键,然后最多删除其中一个键。如果两个或多个线程同时尝试删除相同的键,则可能有一个线程尝试删除一个不存在的键,这对您来说可能是意外的(但不会导致DB损坏)。
  • Redis+sql之间的隐式一致性:看起来您使用的是guids,所以不相关的事情发生冲突的可能性很小。您的示例只包含一个delete操作(很可能不会导致一致性问题),因此很难推测在所有其他情况下redis和sql数据库是否保持一致。通常,如果您的ID从未被重用,您可能是安全的-但是保持两个数据库保持同步是一个很难的问题,而且您很可能在某个地方出错。

但是,您的代码对于它所做的工作似乎过于复杂。如果您希望能够长期保持这种状态,我建议您大大简化它。

  • 不要使用ref参数,除非您真正知道自己在做什么(这里没有必要)。
  • 不要将字符串与其他数据类型混为一谈,因此尽可能避免使用ToString()。当然,要避免像Contains这样的讨厌的技巧来检查密钥是否相等。当一些意想不到的事情发生时,您希望您的代码中断,因为“蹒跚而行”的代码实际上是不可能调试的(您将编写bug)。
  • 如果唯一真正能做的事情是等待所有的任务,那么不要有效地返回一个任务数组--最好在调用中这样做,以简化API。
  • 别用红葡萄酒。这可能只是分散了注意力--您已经有了另一个数据库,所以您不太可能在这里使用它,除了性能方面的原因,而且为假设的性能问题添加整个额外的数据库引擎还为时尚早。需要额外连接的额外开销可能会使代码比只有一个db的代码慢一些,特别是在无法保存许多sql查询的情况下。
票数 2
EN

Stack Overflow用户

发布于 2015-12-03 15:54:24

注意:这个答案是在OP修改他们的问题以添加他们的代码之前发布的,这表明这实际上是一个异步/等待是否是线程安全的问题。

静态方法本身并不是一个问题。如果静态方法是自包含的,并且只能使用局部变量来完成它的工作,那么它是完全线程安全的。

如果静态方法不是自包含的(委托给线程不安全的代码),或者它以非线程安全的方式操作静态状态,即在lock()子句之外访问静态变量,就会出现问题。

例如,int.parse()int.tryParse()是静态的,但是完全线程安全。想象一下,如果它们不是线程安全的话,会有多可怕。

票数 2
EN

Stack Overflow用户

发布于 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,则其中一个线程丢失了),所以当等待它们全部完成时,您可能已经错过了一个任务。

此外,我也会避免混合范式。要么使用异步/等待,要么跟踪一组任务,并让方法添加到该列表中。两样都别做。从长远来看,这将有助于提高代码的可维护性。(注意,线程仍然可以返回一个任务,您收集这些任务,然后等待所有这些任务。但是,收集方法负责任何线程处理问题,而不是隐藏在被调用的方法中)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34070160

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档