【问题标题】:Multiple Threading behaviour多线程行为
【发布时间】:2012-02-29 04:46:53
【问题描述】:

我有以下代码来帮助我理解多线程,它的目的是创建 3 个带有调试代码的后台工作线程来显示线程使用/可用性。 现在代码看起来不错,但有时我会得到意想不到的结果。

调用代码:

    static void Main(string[] args)
            {


                ThreadPool.CreatWorkBetter();

                Console.ReadLine();
            }

实现代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using t = System.Threading;

namespace CSharpConcepts
{
    public static class ThreadPool
    {
        private static t.ManualResetEvent[] resetEvent;

        public static void CreatWorkBetter()
        {
            Console.WriteLine("Start");
            ListAvailableThreads();
            resetEvent = new t.ManualResetEvent[3]; 
            resetEvent[0] = new t.ManualResetEvent(false);
            resetEvent[1] = new t.ManualResetEvent(false);
            resetEvent[2] = new t.ManualResetEvent(false);
            t.ThreadPool.QueueUserWorkItem(
                new t.WaitCallback(delegate(object state)
                                    {
                                        PooledFunc("Stage 1", resetEvent[0]);

                                    }));
            t.ThreadPool.QueueUserWorkItem(
                new t.WaitCallback(delegate(object state)
                {
                    PooledFunc("Stage 2", resetEvent[1]);

                }));
            t.ThreadPool.QueueUserWorkItem(
                new t.WaitCallback(delegate(object state)
                {
                    PooledFunc("Stage 3", resetEvent[2]);

                }));
            t.WaitHandle.WaitAll(resetEvent);
            Console.WriteLine("Finished");
            ListAvailableThreads();
        }

        static void PooledFunc(object state, t.ManualResetEvent e)
        {
            Console.WriteLine("Processing request '{0}'", (string)state);
            // Simulation of processing time
            t.Thread.Sleep(2000);
            Console.WriteLine("Request processed");
            ListAvailableThreads();
            e.Set();
        }

        public static void ListAvailableThreads()
        {
            int avlThreads, avlToAsynThreads;   
            t.ThreadPool.GetAvailableThreads(out avlThreads, out avlToAsynThreads);

            string message = string.Format("Processed request: {3}, From ThreadPool :{0} ,Thread Id :{1},Free Threads :{2}",t.Thread.CurrentThread.IsThreadPoolThread,t.Thread.CurrentThread.ManagedThreadId,avlThreads,t.Thread.CurrentThread.ThreadState);
            Console.WriteLine(message);
        }
    }
}

我所期望的结果是明智的,而且大部分时间我都得到了它(显示的关键线是空闲线程回到 1023 是我真正想看到的):

开始处理的请求:正在运行,来自 ThreadPool :False ,线程 ID :1,空闲线程:1023 处理请求“阶段 1”处理请求 'Stage 2' 处理请求 'Stage 3' 请求已处理 已处理 请求:背景,来自线程池:True,线程 ID:4,空闲线程 :1020 请求已处理处理的请求:后台,来自 ThreadPool :真,线程 ID:3,空闲线程:1021 请求已处理已处理 请求:背景,来自线程池:True,线程 ID:5,空闲线程 :1022 已完成处理的请求:正在运行,来自 ThreadPool :False ,Thread Id :1,Free Threads :1023

但是,我有时会看到显示 1022 的空闲线程,我希望它是 1023,因为 3 个线程已经完成了工作,所以它们应该已经返回到线程池:

开始处理的请求:正在运行,来自 ThreadPool :False ,线程 ID :1,空闲线程:1023 处理请求“阶段 1”处理请求 'Stage 2' 处理请求 'Stage 3' 请求已处理 已处理 请求:背景,来自线程池:True,线程 ID:3,空闲线程 :1020 请求已处理处理的请求:后台,来自 ThreadPool :真,线程 ID:4,空闲线程:1020 请求已处理已处理 请求:背景,来自线程池:True,线程 ID:5,空闲线程 :1022 已完成处理的请求:正在运行,来自线程池:False,线程 ID:1,空闲线程:1022

有什么想法吗?

【问题讨论】:

  • 您确定这是您的工作线程之一不是免费的吗?会不会是垃圾收集器?

标签: c# .net multithreading


【解决方案1】:

你的最后一个线程调用e.Set(),因为它是最后一行,这将主线程从它的WaitAll()中释放出来,但是这并不意味着最后一个工作线程已经退出CreatWorkBetter方法了。有时,主线程会唤醒并计算活动工作线程的数量,甚至在最后一个工作线程从e.Set() 调用中退出该方法之前。

所以我的最后一段是:

这是理解线程的关键——除非你使用同步构造来控制每个线程的状态,否则你不能从一个方法中对其他线程的相对状态做出假设。

【讨论】:

  • Rubans 正在在主线程中 CreatWorkBetter 的末尾调用 ListAvailableThreads
  • 嗨,克里斯,感谢您的回复。我假设您的意思是 1023,因为这是 .net 4 中可用的最大线程数。此外,我确实将 ListAllAvailableThreads 移到了外面,仍然是同样的问题。即使从原始代码的输出中,仍然显示 From ThreadPool :False 所以我假设这是从 ThreadPool 外部调用的。
  • @Rubans 抱歉,这显然不是问题所在。我错过了您在 CreatWorkBetter 方法中调用它的事实。
  • @ChrisShain,我明白你在说什么,这确实有道理,我会做一些研究来检查这一点。有什么建议吗?
  • ListAvailableThreads 中加入一个布尔参数。从 CreatWorkBetter 方法传递 true,从 PooledFunc 传递 false。在string message = string.Format("Processed request... 行上放置一个条件断点,只有在参数为真且Free Threads = 1022 时才会中断。最后,运行它,直到遇到断点。此时,检查调试器的线程窗口中的线程。
【解决方案2】:

您遇到了两个线程已终止且一个线程在PooledFunc 结束时调用e.Set() 但尚未终止的情况。同时主线程的WaitAll(resetEvent)已经返回,主线程正在调用ListAvailableThreads()

【讨论】:

  • 我相信你是对的,这确实有道理,这就是我的想法,但我对线程的理解是基本的。关于我可以做些什么来解决它的任何建议?
  • 为什么需要解决这个问题?你想解决什么问题?为什么你需要确切地知道所有线程都完成了工作——它是一个工作线程池,给你一个抽象。
  • 除了我自己的强迫症外,我真的不需要让它返回 1023 来满足任何功能需求 :)
  • 好的:)。然后我的建议是:忍受它,这是设计使然。您的工作人员代表在完成工作后会发出事件信号。当委托终止时,线程被标记为空闲。两者几乎相同,但又有所不同。如果你想改变,你可以尝试使用 Task 类。或者,如果你想要它,你可以自己启动线程并等待它们终止。
  • 是的,没关系。我开始研究它是为了开始在 c#4.5 中使用 Tasks,但我想至少对线程有一个基本的了解,在我查看 Tasks 和 Async 的东西之前,我认为我现在已经有了。
【解决方案3】:

我没有在 C# 中尝试过多的线程,但我在 Java 中做了一些不错的工作。我会假设对 GetAvailableThreads 的调用不是原子的,也不是锁定的,因此线程可以同时更改计数。

【讨论】:

    猜你喜欢
    • 2015-11-05
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多