C#并行。用于不执行每个步骤

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

我一直在为导入服务制作一个模型,该服务目前按顺序运行。然而,我的模型似乎出现了一个奇怪的问题,有时for循环中有一两个项目没有执行

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();
    }
}