【问题标题】:C# Oracle connection issue while using tasks and blocking collection使用任务和阻止收集时出现 C# Oracle 连接问题
【发布时间】:2014-12-30 18:23:22
【问题描述】:

我有一些任务(nWorkers = 3):

var taskFactory = new TaskFactory(cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskContinuationOptions.LongRunning, TaskScheduler.Default);

for (int i = 0; i < nWorkers; i++)
{
    var task = taskFactory.StartNew(() => this.WorkerMethod(parserItems, cancellationTokenSource));
    tasks[i] = task;
}    

任务调用的方法如下:

protected override void WorkerMethod(BlockingCollection<ParserItem> parserItems, CancellationTokenSource cancellationTokenSource)
{
...log-1...
using (var connection = new OracleConnection(connectionString))
{
    OracleTransaction transaction = null;
    try
    {
        cancellationTokenSource.Token.ThrowIfCancellationRequested();
        connection.Open();
        ...log-2...
        transaction = connection.BeginTransaction();
        ...log-3...
        using (var cmd = connection.CreateCommand())
        {                   
            foreach (var parserItem in parserItems.GetConsumingEnumerable(cancellationTokenSource.Token))
            {
                cancellationTokenSource.Token.ThrowIfCancellationRequested();
                try
                {
                    foreach (var statement in this.ProcessRecord(parserItem))
                    {                               
                        cmd.CommandText = statement;
                        try
                        {                                   
                            cmd.ExecuteNonQuery();                                  
                        }
                        catch (OracleException ex)
                        {
                            ...log-4...
                            if (!this.acceptedErrorCodes.Contains(ex.Number))
                            {
                                throw;
                            }
                        }
                    }
                }
                catch (FormatException ex)
                {
                    log.Warn(ex.Message);
                }
            }
            if (!cancellationTokenSource.Token.IsCancellationRequested)
            {                       
                transaction.Commit();                       
            }
            else
            {                       
                throw new Exception("DBComponent has been canceled");
            }
        }
    }
    catch (Exception ex)
    {
        ...log-5...
        cancellationTokenSource.Cancel();
        if (transaction != null)
        {
            try
            {
                transaction.Rollback();
                ...log-6...
            }
            catch (Exception rollbackException)
            {
                ...log-7...
            }
        }
        throw;
    }
    finally
    {
        if (transaction != null)
        {
            transaction.Dispose();
        }
        connection.Close();
        ...log-8...
    }
}
...log-9... 
}

有一个 ParserItem 对象的生产者,这些是消费者。通常它工作正常,有时会出现 Oracle 连接超时,但在这些情况下,我可以看到异常消息并且一切都按设计工作。

但有时该过程会卡住。当它卡住时,在日志文件中我可以看到 log-1 消息,然后(大约 15 秒后)我看到 log-8 消息,但是让我抓狂的是为什么我看不到异常消息日志-5 也不是 log-9 消息。 由于从不调用 cancelTokenSource.Cancel() 方法,因此有界集合的项目生产者会被卡住,直到两小时后超时。

它是为 NET Framework 4 编译的,我使用 Oracle.ManagedDataAccess 库进行 Oracle 连接。

任何帮助将不胜感激

最好的问候, 谢谢

【问题讨论】:

  • 您是否有可能只是遇到数据库端死锁?我建议使用提示告诉 ORACLE 您对数据读取和更新的首选锁定策略。
  • 您需要进行挂起转储。我推荐使用procdump。然后,您可以使用 SOS.dll 将转储加载到 WinDBG 中并开始查看。有很多很棒的入门教程。
  • @Darek 我知道在数据库端死锁的情况下,我应该捕获并查看 Oracle 异常,而不是只查看 finally 子句的执行。是这样吗?还是我错过了什么
  • 您可能遇到了一些内存死锁,因为在您的代码中,您从 catch 块中取消,这本身会引发异常。底部,行,错误的代码。尝试清理,然后再次调查。

标签: c# oracle exception task producer-consumer


【解决方案1】:

当您使用 using 范围时,您不应该释放事务或连接。其次,您应该很少依赖基于异常的编程风格。您的代码重写如下:

using (var connection = new OracleConnection(connectionString))
{
    using (var transaction = connection.BeginTransaction())
    {
        connection.Open();
        //...log-2...
        using (var cmd = connection.CreateCommand())
        {
            foreach (var parserItem in parserItems.GetConsumingEnumerable(cancellationTokenSource.Token))
            {
                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    try
                    {
                        foreach (var statement in ProcessRecord(parserItem))
                        {
                            cmd.CommandText = statement;
                            try
                            {
                                cmd.ExecuteNonQuery();
                            }
                            catch (OracleException ex)
                            {
                                //...log-4...
                                if (!acceptedErrorCodes.Contains(ex.ErrorCode))
                                {
                                    log.Warn(ex.Message);
                                }
                            }
                        }
                    }
                    catch (FormatException ex)
                    {
                        log.Warn(ex.Message);
                    }
                }
            }
            if (!cancellationTokenSource.IsCancellationRequested)
            {
                transaction.Commit();
            }
            else
            {
                transaction.Rollback();
                throw new Exception("DBComponent has been canceled");
            }
        }
    }
}
//...log-9... 

如果这有帮助,请告诉我。

【讨论】:

  • 我重构了代码,但由于我无法在我的计算机中重现此问题,因此需要一些时间来验证错误是否消失。一旦我得到确认,我会选择你的答案作为接受。同时,非常感谢您的帮助:)
猜你喜欢
  • 2019-12-14
  • 2019-04-05
  • 2020-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-28
  • 1970-01-01
  • 2010-12-11
相关资源
最近更新 更多