DataLayer中的并发异常

时间:2018-02-27 13:43:42

标签: c# multithreading asp.net-core .net-core

我有一个项目列表,对于每个项目,我需要执行一系列任务。 为了访问数据层,我使用以下代码:

public async Task<ExampleResult> GetExampleResultAsync(Parameter parameter, CancellationToken cancellationToken = default(CancellationToken))      
{
    GetCustomerResult result = null;
    OracleConnection connection = this.Database.GetOracleConnection(); 
    bool needClose = false;
    if (connection.State != ConnectionState.Open)
    {
        await connection.OpenAsync(cancellationToken);
        needClose = true;
    }

    try
    {
        using (OracleCommand cmd = connection.CreateCommand())
        {
            .... do the work
        }
    }
    finally
    {
        if (needClose)
            connection.Close();
    }

    return result;
}

但是,这会导致并发,因此我得到了

  

连接未关闭错误

我最终的可能解决方法是更改​​连接数据库的方式。我想为每个请求使用新的连接实例,并使用using语句包围这些实例。

这会带给我很多工作,我想了解一下处理并发问题的最佳实践。

编辑:

我的来电者功能如下所示

public async Task<DomainResult<IList<MbRiskDto>>> QueryAsync(Action<MbrAccountAutoMatcherQueryParameter> parameter,
    CancellationToken cancellationToken = default(CancellationToken))
{
    parameter(_parameter);
    var nonDeclaredMbrAccounts = await _nonDeclaredMbrAccountsQuery.QueryAsync(param => param.TransactionDate = _parameter.TransactionDate, cancellationToken);

    if (nonDeclaredMbrAccounts.IsFailed)
        nonDeclaredMbrAccounts.Errors.ForEach(error => _errors.Add(error));

    var taskList = new List<Task<MbRiskDto>>();
    var throttler = new SemaphoreSlim(initialCount: 10);

    foreach (var nonDeclaredAccount in nonDeclaredMbrAccounts.Result)
    {
        await throttler.WaitAsync(cancellationToken);

        var account = (AccountDto) nonDeclaredAccount.Clone();
        var firstAccountHolder = Convert.ToInt32(account.AccountHolders.FirstOrDefault());

        var task = Task.Run(async () =>
        {
            MbRiskDto result;
            try
            {
                result = new MbRiskDto
                {
                    KimNo = await GetKimNo(firstAccountHolder),
                    HesNo = account.AccountNo,
                    FinCode = await GetFinanceCode(account, firstAccountHolder, cancellationToken),
                    Unvan = account.SMA.Substring(0, Math.Min(account.SMA.Length, 54))
                };
            }
            finally
            {
                throttler.Release();
            }

            return result;
        }, cancellationToken);

        taskList.Add(task);
    }

    var taskResult = await Task.WhenAll(taskList);

    return DomainResult<IList<MbRiskDto>>.Success(taskResult);
}

1 个答案:

答案 0 :(得分:0)

根据评论我删除了Task.Run()子句并替换为下面的代码。现在,我相信我的并发问题已经解决了。

Func<Task<MbRiskDto>> mbRiskTask = async ()  => new MbRiskDto
{
    KimNo = await GetKimNo(firstAccountHolder, cancellationToken),
    HesNo = account.AccountNo,
    FinCode = await GetFinanceCode(account, firstAccountHolder, cancellationToken),
    Unvan = account.SMA.Substring(0, Math.Min(account.SMA.Length, 54))
};


taskList.Add(mbRiskTask.Invoke());

因此,结果代码可以在下面看到

public async Task<DomainResult<IList<MbRiskDto>>> QueryAsync(Action<MbrAccountAutoMatcherQueryParameter> parameter,
    CancellationToken cancellationToken = default(CancellationToken))
{
    parameter(_parameter);
    var nonDeclaredMbrAccounts = await _nonDeclaredMbrAccountsQuery.QueryAsync(param => param.TransactionDate = _parameter.TransactionDate, cancellationToken);

    if (nonDeclaredMbrAccounts.IsFailed)
        nonDeclaredMbrAccounts.Errors.ForEach(error => _errors.Add(error));

    var taskList = new List<Task<MbRiskDto>>();

    foreach (var nonDeclaredAccount in nonDeclaredMbrAccounts.Result)
    {
        var account = (AccountDto) nonDeclaredAccount.Clone();
        var firstAccountHolder = Convert.ToInt32(account.AccountHolders.FirstOrDefault());

        Func<Task<MbRiskDto>> mbRiskTask = async ()  => new MbRiskDto
        {
            KimNo = await GetKimNo(firstAccountHolder, cancellationToken),
            HesNo = account.AccountNo,
            FinCode = await GetFinanceCode(account, firstAccountHolder, cancellationToken),
            Unvan = account.SMA.Substring(0, Math.Min(account.SMA.Length, 54))
        };
        //var task = Task.Run();

        taskList.Add(mbRiskTask.Invoke());
    }

    var taskResult = await Task.WhenAll(taskList);

    return DomainResult<IList<MbRiskDto>>.Success(taskResult);
}

编辑:

通过这种重构,操作大约需要4分钟才能完成。你有什么建议可以让它更快地运作吗?