【问题标题】:Can somebody explain this odd behavior when working with ThreadPool?有人可以在使用 ThreadPool 时解释这种奇怪的行为吗?
【发布时间】:2012-04-03 12:02:59
【问题描述】:

代码

using System;
using System.Threading;

public delegate void LoadingProgressCallback(double PercentComplete,string ItemName);
public delegate void LoadCompleteCallback(int ItemID, string ItemName);

public static class Program
{
    public static void Main(string[] args)
    {
        LoadTest loadTest = new LoadTest();
        loadTest.LoadItems(args);
    }
}

public class LoadTest
{       
    ManualResetEvent resetEvent;
    int numThreads = 0;

    public LoadTest()
    {}

    public void LoadItems(string[] Items)
    {
        numThreads = 0;
        resetEvent = new ManualResetEvent(false);

        foreach(string item in Items)
        {
            Console.WriteLine("Adding {0} to ThreadPool",item);
            ThreadPool.QueueUserWorkItem
            (
                delegate
                {
                    Load(item, this.progCall, this.compCall);
                }
            );
            numThreads++;

            Thread.Sleep(100);//Remove this line

        }
        resetEvent.WaitOne();
    }

    public void progCall(double PercentComplete, string ItemName)
    {
        Console.WriteLine("{0}: is {1}% Complete [THREAD:{2}]",ItemName,PercentComplete.ToString(),Thread.CurrentThread.ManagedThreadId.ToString());
    }
    public void compCall(int ItemID, string ItemName)
    {
        Console.WriteLine("{0}: is Complete",ItemName);
        numThreads--;
        if(numThreads == 0)
        {
            resetEvent.Set();
        }
    }

    public void Load(string Item, LoadingProgressCallback progressCallback, LoadCompleteCallback completeCallback)
    {
        Console.WriteLine("Loading: {0} [THREAD:{1}]",Item,Thread.CurrentThread.ManagedThreadId.ToString());

        for(int i = 0; i <= 100; i++)
        {
            if(progressCallback != null)
            {
                progressCallback((double)i, Item);
            }
            Thread.Sleep(100);
        }
        if(completeCallback != null)
        {
            completeCallback(0,Item);
        }
    }
}

观察

如果我从命令行运行这个程序,像这样......

&gt;TheProgram item1 item2

输出将如下所示。

将 item1 添加到线程池
正在加载:项目 1 [线程:3]
项目 1:为 0% 完成 [线程:3]
将 item2 添加到线程池
正在加载:项目 2 [线程:4]
项目 2:完成率为 0% [线程:4]
项目 1:已完成 1% [线程:3]
项目 2:已完成 1% [线程:4]
项目 1:已完成 2% [线程:3]
项目 2:已完成 2% [线程:4]

但是,如果我删除这一行。

Thread.Sleep(100);//Remove this line

LoadItems 方法的输出如下所示。

将 item1 添加到线程池
将 item2 添加到线程池
正在加载:项目 2 [线程:4]
正在加载:项目 2 [线程:3]
项目 2:完成率为 0% [线程:4]
项目 2:完成率为 0% [线程:3]
项目 2:已完成 1% [线程:4]
项目 2:已完成 1% [线程:3]
项目 2:已完成 2% [线程:3]
item2:完成率为 2% [THREAD:4]

问题

似乎使用了两个线程,尽管它们似乎都在作用于相同的数据。为什么代码会这样?

【问题讨论】:

    标签: c# multithreading threadpool


    【解决方案1】:

    您正在关闭循环变量,这会给您带来意想不到的结果。试试这个:

    foreach(string item in Items)
    {
        string item2 = item;
        Console.WriteLine("Adding {0} to ThreadPool", item2);
        ThreadPool.QueueUserWorkItem
        (
            delegate
            {
                Load(item2, this.progCall, this.compCall);
            }
        );
        numThreads++;
    
        Thread.Sleep(100);//Remove this line
    
    }
    

    参考文献

    【讨论】:

    • +1 很好,我盯着代码看了 5 分钟,根本没看到。
    • +1。仅此问题就必须至少有 500 个问题。到目前为止,我已经回答了一些。
    【解决方案2】:

    看到代码后立即想到的一件事是没有使用Interlocked

    你必须使用it 否则你会看到奇怪的错误和行为。

    所以不是

    numThreads++;
    

    用途:

    Interlocked.Increment(ref numThreads);
    

    【讨论】:

    • 然后我会在compCall 方法中调用Interlocked.Decrement(ref numThreads) 吗?
    猜你喜欢
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 2011-04-30
    • 2016-03-08
    • 2016-09-18
    • 2013-11-30
    • 1970-01-01
    相关资源
    最近更新 更多