我有一些任务(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消息,但让我发疯的是,为什么我看不到异常消息log-5和log-9消息。因为从来没有调用过cancellationTokenSource.Cancel()方法,所以有界集合的项目生产者会被阻塞,直到两个小时后超时。
它是为NETFramework4编译的,我使用Oracle.ManagedDataAccess库来连接。
任何帮助都将不胜感激。
发布于 2014-12-30 20:36:33
使用作用域时,永远不应释放事务或连接。其次,您应该很少依赖基于异常的编程风格。下面重写了您的代码:
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... 如果这有帮助的话请告诉我。
发布于 2022-09-20 10:50:53
我可以确认你所说的一切。(程序卡住,CPU使用率低,甲骨文连接超时等)
解决方法之一是使用线程而不是任务。
更新:经过仔细调查后,我发现当您使用大量任务时,由Oracle驱动程序排队的ThreadPool工作线程的启动变得缓慢,最终导致(假的)连接超时。
以下是几个解决方案:
解决方案1:增加线程池的最小线程数,例如:
ThreadPool.SetMinThreads(50, 50); // YMMV或
解决方案2:将您的连接配置为使用池并适当地设置其最小大小。
var ocsb = new OracleConnectionStringBuilder();
ocsb.DataSource = ocsb.DataSource;
ocsb.UserID = "myuser";
ocsb.Password = "secret";
ocsb.Pooling = true;
ocsb.MinPoolSize = 20; // YMMV重要事项:在调用任何创建大量任务的例程之前,请使用“热身”池打开单个连接:
using(var oc = new OracleConnection(ocsb.ToString()))
{
oc.Open();
oc.Close();
}注意: Oracle通过连接字符串(删除密码)对连接池进行索引,因此,如果要打开其他连接,则必须始终使用完全相同的连接字符串。
https://stackoverflow.com/questions/27710525
复制相似问题