C#并行。用于不执行每个步骤
我一直在为导入服务制作一个模型,该服务目前按顺序运行。然而,我的模型似乎出现了一个奇怪的问题,有时for循环中有一两个项目没有执行C#并行。用于不执行每个步骤,c#,parallel-processing,parallel.for,C#,Parallel Processing,Parallel.for,我一直在为导入服务制作一个模型,该服务目前按顺序运行。然而,我的模型似乎出现了一个奇怪的问题,有时for循环中有一两个项目没有执行 class Service { private Thread _worker; private bool _stopping; private CancellationTokenSource _cts; private ParallelOptions _po; private Repository _repos
class Service
{
private Thread _worker;
private bool _stopping;
private CancellationTokenSource _cts;
private ParallelOptions _po;
private Repository _repository;
public void Start(Repository repository)
{
_repository = repository;
_cts = new CancellationTokenSource();
_po = new ParallelOptions {
CancellationToken = _cts.Token
};
_worker = new Thread(ProcessImport);
_worker.Start();
}
public void Stop()
{
_stopping = true;
_cts.Cancel();
if(_worker != null && _worker.IsAlive)
_worker.Join();
}
private void ProcessImport()
{
while (!_stopping)
{
var import = _repository.GetInProgressImport();
if (import == null)
{
Thread.Sleep(1000);
continue;
}
try
{
Parallel.For(0, 1000, _po, i => Work.DoWork(i, import, _cts.Token, _repository));
}
catch (OperationCanceledException)
{
// Unmark batch so it can be started again
batch = _repository.GetBatch(import.BatchId);
batch.Processing = false;
_repository.UpdateBatch(batch);
Console.WriteLine("Aborted import {0}", import.ImportId);
}
catch (Exception ex)
{
Console.WriteLine("Something went wrong: {0}", ex.Message);
}
}
}
}
class Work
{
public static void DoWork(int i, Import import, CancellationToken ct, Repository repository)
{
// Simulate doing some work
Thread.Sleep(100);
HandleAbort(ct);
Thread.Sleep(100);
HandleAbort(ct);
Thread.Sleep(100);
// Update the batch
var batch = repository.GetBatch(import.BatchId);
batch.Processed++;
if (batch.Processed == batch.Total)
{
batch.Finished = DateTime.Now;
batch.Processing = false;
}
repository.UpdateBatch(batch);
}
private static void HandleAbort(CancellationToken ct)
{
if (!ct.IsCancellationRequested)
return;
ct.ThrowIfCancellationRequested();
}
}
有了这段代码,我经常发现批处理永远不会完成,并且批处理=999或998
谁能说明我做错了什么
提前谢谢
编辑:
为了明确存储库/批处理对象,我相信在我当前的模型中,它是线程安全的
class Repository
{
private ConcurrentBag<Batch> _batchData = new ConcurrentBag<Batch>();
private ConcurrentBag<Import> _importData = new ConcurrentBag<Import>();
public void CreateImport(Import import)
{
_importData.Add(import);
}
public Import GetInProgressImport()
{
var import = _importData
.Join(_batchData, i => i.BatchId, b => b.BatchId, (i, b) => new
{
Import = i,
Batch = b
})
.Where(j => j.Batch.Processed < j.Batch.Total && !j.Batch.Processing)
.OrderByDescending(j => j.Batch.Total - j.Batch.Processed)
.ThenBy(j => j.Batch.BatchId - j.Batch.BatchId)
.Select(j => j.Import)
.FirstOrDefault();
if (import == null)
return null;
// mark the batch as processing
var batch = GetBatch(import.BatchId);
batch.Processing = true;
UpdateBatch(batch);
return import;
}
public List<Import> ListImports()
{
return _importData.ToList();
}
public void CreateBatch(Batch batch)
{
_batchData.Add(batch);
}
public Batch GetBatch(Int64 batchId)
{
return _batchData.FirstOrDefault(b => b.BatchId == batchId);
}
public void UpdateBatch(Batch batch)
{
var batchData = _batchData.First(b => b.BatchId == batch.BatchId);
batchData.Total = batch.Total;
batchData.Processed = batch.Processed;
batchData.Started = batch.Started;
batchData.Finished = batch.Finished;
batchData.Processing = batch.Processing;
}
}
class Import
{
public Int64 ImportId { get; set; }
public Int64 BatchId { get; set; }
}
class Batch
{
public Int64 BatchId { get; set; }
public int Total { get; set; }
public int Processed { get; set; }
public DateTime Created { get; set; }
public DateTime Started { get; set; }
public DateTime Finished { get; set; }
public bool Processing { get; set; }
}
根据,Parallel的重载。对于
,将第二个整数指定为toExclusive
,表示上升到但不满足该值。换句话说,999是预期的结果,而不是1000-但还要注意,从“0”开始,循环执行1000次
乍一看,您的代码是并行的,因此请确保您没有看到“999”调用与“998”调用的顺序不符-这是因为并行执行时,您的代码本质上是无序的,并且很容易被随机重新排列。另外,请继续阅读,因为您的代码可能正在访问它应该等待的值。看起来像批处理中丢失的更新。增量不是原子的<代码>批处理++代码>是活泼的。使用
联锁。增量
在我看来,你现在对线程还不是很了解。在没有很好理解的情况下执行如此复杂的线程是非常危险的。您所犯的错误很难测试,但生产部门会发现它们。您是否正在更新线程之间的共享批次?如果是,则需要同步共享状态。我看不到任何类型的同步。您的存储库是线程安全的吗?特别是GetBatch和UpdateBatch之间没有任何锁的部分看起来很奇怪。上面的说明说明了这一点吗?请参见上面的编辑。我在计算迭代次数,而不是变量I的值。仍然在学习——因此它不是生产代码。看起来问题还扩展到另一个线程可能会改变批处理以检索它。在这种情况下,我认为应该锁定整个更新批处理部分。这基本上取代了对联锁增量的需求,对吗?谢谢。审核代码中并发访问数据且至少有一个并发编写器的位置<代码>已处理
就是这样一个地方。
class Work
{
private static object _sync = new object();
public static void DoWork(int i, Import import, CancellationToken ct, Repository repository)
{
// Do work
Thread.Sleep(100);
HandleAbort(ct);
Thread.Sleep(100);
HandleAbort(ct);
Thread.Sleep(100);
lock (_sync)
{
// Update the batch
var batch = repository.GetBatch(import.BatchId);
batch.Processed++;
if (batch.Processed == batch.Total)
{
batch.Finished = DateTime.Now;
batch.Processing = false;
}
repository.UpdateBatch(batch);
}
}
private static void HandleAbort(CancellationToken ct)
{
if (!ct.IsCancellationRequested)
return;
ct.ThrowIfCancellationRequested();
}
}