C#比赛条件处理

C#比赛条件处理,c#,multithreading,race-condition,C#,Multithreading,Race Condition,虽然我意识到有很多这样的问题,但我找不到具体适用于我的案例的东西。所以我想我会冒着复制的风险来得到我问题的答案 背景信息: 我有一个主对象(ActionsMaster),它包含一系列子对象作为属性并处理高级方法。我的“处理表单”将处理将此列表中的每个操作应用到环境中的代码,但由于应用操作的调用可以是多线程的/一次支持多个调用,我认为我将是高效的,一次生成大约5或10个线程(想想这里的数千个操作…我希望这是快速的) 因此,为了支持这一点,我在我的子对象类型中添加了一个Status属性,表示它是否已

虽然我意识到有很多这样的问题,但我找不到具体适用于我的案例的东西。所以我想我会冒着复制的风险来得到我问题的答案

背景信息:

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

因此,为了支持这一点,我在我的子对象类型中添加了一个Status属性,表示它是否已被传递给线程进行操作,或者它是否可以自由使用,以及是否向ActionsMaster添加了一个用于锁定的对象

到目前为止的代码:

下面是我用来确定某个操作是否可用的代码,这就是我的问题的来源:我可以将对象返回到锁块内吗?或者我必须说明,如果我返回锁块,它将永远不会到达锁块的末尾吗?我需要在运行LINQ语句之前锁定,否则两个线程可能会得到相同的结果。。。以下是我所拥有的:

    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();
            }
        }
    }
这是实现我想要实现的目标的正确方法吗?

是.Net framework的v4版本,它将为您处理所有这些问题。学习如何使用它会有一点开销,但最终我认为你会更喜欢学习一个经过尝试、测试和标准的库,而不是花时间自己制作

基本上,您可以将任务交给it来执行,it将处理对它们进行排队并在池中的线程上运行它们的问题

关于使用TPL,尽管其中的代码不是很清楚。
这可能更清楚。

将工作转移到多个线程并不总是解决办法。 当试图将工作移到后台线程时,应始终考虑您的机器有多少内核。在一台有2个内核的机器上启动5-10个线程将导致大量上下文切换,这可能会降低代码的性能

现在,我们来看看解决方案。如果您的目标是.NET4.0或更高版本,我建议您使用ConcurrentQueue和TPL的组合。您的代码可以如下所示:

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
            }
        }
private ConcurrentQueue\u actionFileQueue=new ConcurrentQueue();
内部操作FileAction GetNextFreeAction(CancellationToken CancellationToken=default(CancellationToken))
{
//检查是否请求取消
cancellationToken.ThrowIfCancellationRequested();
ActionFileActionFileToHandle;
_TryDequeue(out actionFileToHandle);
//如果无法退出队列,则可能返回null
返回actionFileToHandle;
}
//C#方法应为不带下划线的大小写
void HandleAllActionFiles()
{
尝试
{
var cancellationToken=新的CancellationTokenSource().Token;
并行调用(
新的并行选项
{
MaxDegreeOfParallelism=Environment.ProcessorCount,
CancellationToken=CancellationToken
},()=>GetNextFreeAction());
}
捕捉(操作取消异常e)
{
//做些取消的事情
}
}

当然,您仍然需要处理retrys并不断地倾听队列以查看是否有项目正在等待,但这可以让您开始:)

简而言之,不是。您正在为每个操作创建一个新线程。您应该重新使用线程,而不是不断地拆下它们并创建新的线程,因为这些操作非常昂贵。有一些工具/库可以为您处理这些类型的事情。您希望使用线程池(不需要自己为线程池编写代码)。您的队列在实现上也非常低效,但从您所展示的情况来看,它并不是不安全的。您能否提供一些示例,说明正确的方法是什么?基本上,我希望一次运行10个线程,并且希望,是的,重复使用相同的线程/内存中已有的后台工作线程来应用下一个操作。我明白你的意思,但我不知道如何重新使用内存,只是代码块。。。如果您也有关于如何构建一个高效队列的示例,而这不是我所做的,请提供我认为使用泛型有效的示例…--那么,在lock()块中返回对象不会引起问题吗?不,对于这个网站来说,这个主题太宽泛了。坐下来看书,上课,或者阅读一些相关的教程/博客/文章。@Servy:使用默认同步提供程序的对象是否依赖于
线程池而不是每次生成新线程?我真的很想知道在Lock()块中返回我的对象是否会导致问题。。。但这也很好。谢谢不,这不会引起问题。当您离开锁块的作用域时,锁将被释放,无论您如何离开。我认为在封面下,lock语句基本上是一个try…finally块,它执行一个监视器。在try和Monitor之后输入(o)。在finally中退出(o)。顺便说一句,你是对的:我尝试使用自己的后台工作人员运行代码,实际上它比我一个接一个地运行操作时慢。。。真奇怪@MaxOvrdrv在分配新线程时,操作系统会执行一系列工作,例如分配新堆栈、设置新线程上下文、新Thr