c# - 从 ASP.net 项目调用静态异步方法

标签 c# asp.net

我想知道这种情况是否是线程安全的,是否存在我目前未发现的问题:

  1. 我从 ASP.net Controller 调用非静态类的非静态方法(这个类在另一个项目中,类被注入(inject)到 Controller 中)。

  2. 这个方法(非静态的)做一些工作并调用一些其他静态方法传递给它 userId

  3. 最后静态方法做了一些工作(为此需要 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/

相关文章:

asp.net - 在 asp.net/Azure 中创建虚拟路径

c# - 在有很多类需要动态生成其他类的项目中进行依赖注入(inject)

c# - NUnit 比较两个列表

asp.net - Linq,如何检查字段的值是否为空

c# - DropDownListForModel 无法添加 HTML 属性

javascript - 动态加载局部 View 动态加载另一个局部 View

c# - 防止多次调用 Web 服务

c# - 强制所有 WPF 绑定(bind)控件使用 GetBindingExpression().UpdateTarget() 进行更新

c# - 如何创建一个循环遍历列表并将要访问的数据成员作为输入参数的函数

c# - 在 sql server 中表示 bool 数据