C#竞赛条件处理

时间:2014-04-07 14:15:50

标签: c# multithreading race-condition

虽然我意识到那里有很多这些,但我找不到适用于我的情况的东西。所以我认为我冒了重复的风险来得到我的问题的答案......

背景:

我有一个主对象(ActionsMaster),它包含一个子对象列表作为属性并处理高级方法。我的处理表格"将要处理代码以将此列表中的每个操作应用于环境,但由于应用操作的调用可以是多线程/一次支持多个调用,我认为我是高效的一次产生大约5或10个线程(想想这里成千上万的动作......我希望这个快速)。

所以为了支持这一点,我已经为我的子对象类型添加了一个Status属性,说它是否被移交给一个线程进行操作,或者它是否是免费的使用以及向ActionsMaster添加一个对象以用于锁定目的。

到目前为止的代码:

以下是我用来确定某个操作是否可用的代码,这就是我的问题所在:我可以在锁定区块内返回该对象,还是我必须考虑它是否已经过了如果我回到那里,永远不会到达锁定区的尽头?我需要在运行LINQ语句之前锁定,否则2个线程可能会得到相同的结果......这里是我的拥有:

    internal ActionFileAction GetNextFreeAction()
    {
        lock (_IsLocked)
        {
            ActionFileAction action = this.Actions.DefaultIfEmpty(null).FirstOrDefault(a => a.Status == null || a.Status == Action_Status.Free);
            if (action != null)
                action.Status = Action_Status.Queued;

            return action;
        }
    }

我会在后台工作者中调用它,如下所示:

    void bkg_Automatic_DoWork(object sender, DoWorkEventArgs e)
    {
        //check for user cancel...
        if (this.CancelJobs || this.IsAbort)
        {
            e.Cancel = true;
        }
        else
        {
            if (!this.IsFinished)
            {
                ActionFileAction action = this.TheFile.GetNextFreeAction();
                if (action == null)
                {
                    this.IsFinished = true;
                }
                else
                {
                    //PROCESS THE ACTION HERE...
                }
            }
        }
        e.Result = true;
    }

只是为了彻底,这是我的RunWorkerCompleted代码:

    void bkg_Automatic_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            GenericLogEntry ent = new GenericLogEntry();
            ent = new GenericLogEntry();
            ent.Area = "SYSTEM";
            ent.Action = "USER ABORT";
            ent.Description = this.CancelJobs == true ? "CLOSED FORM" : "PUSHED ABORT BUTTON";
            ent.Stamp = DateTime.Now;
            ent.Status = LogEntryStatus.Fail;
            UpdateLogView(ent);
        }
        else
        {
            if (this.IsFinished)
            {
                //add all processed actions to the deployment results, if not already done.
            }
            else
            {
                //spawn new thread from this one's END... 
                BackgroundWorker bkg1 = new BackgroundWorker();
                bkg1.DoWork += new DoWorkEventHandler(bkg_Automatic_DoWork);
                bkg1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bkg_Automatic_RunWorkerCompleted);
                bkg1.WorkerSupportsCancellation = true;
                bkg1.RunWorkerAsync();
            }
        }
    }

这是我正在努力完成的正确方法吗?

2 个答案:

答案 0 :(得分:1)

Task Parallel Library附带.Net框架的v4,并将为您处理所有这些。学习如何使用它有一点点开销,但最终我认为你喜欢学习一个久经考验的标准库,而不是花时间自己制作它。

基本上你可以把它的任务交给执行,它会处理它们的排队并在池中的线​​程上运行它们。

Here is an MSDN tutorial关于使用TPL,虽然其中的代码不是很清楚。 这里的another article可能更清楚。

答案 1 :(得分:1)

将工作卸载到多个线程并不总是答案。 在尝试将工作转移到后台线程时,您应该始终考虑您的计算机具有多少核心。在具有2个内核的计算机上启动5-10个线程将导致大量上下文切换,这可能最终降低代码性能。

现在,解决方案。如果您的目标是.NET 4.0或更高版本,我建议您使用带有TPL组合的ConcurrentQueue。你的代码看起来像这样:

private ConcurrentQueue<ActionFileAction> _actionFileQueue = new ConcurrentQueue<ActionFileAction>();
        internal ActionFileAction GetNextFreeAction(CancellationToken cancellationToken = default(CancellationToken))
        {
            // Checks if a cancellation was requested
            cancellationToken.ThrowIfCancellationRequested();
            ActionFileAction actionFileToHandle;
            _actionFileQueue.TryDequeue(out actionFileToHandle);

            //may return null if couldn't dequeue
            return actionFileToHandle;
        }

        //C# Methods should be CamelCase without underscores
        void HandleAllActionFiles()
        {
            try
            {
                var cancellationToken = new CancellationTokenSource().Token;
                Parallel.Invoke(
                    new ParallelOptions
                    {
                        MaxDegreeOfParallelism = Environment.ProcessorCount,
                        CancellationToken = cancellationToken
                    }, () => GetNextFreeAction());
            }
            catch (OperationCanceledException e)
            {
                // Do stuff with cancellation
            }
        }

当然,您仍然需要处理重试并不断收听队列以查看某个项目是否在等待,但这可以让您开始:)