当前位置: 代码迷 >> C# >> [本拉登]请进,多线程利用有关问题!
  详细解决方案

[本拉登]请进,多线程利用有关问题!

热度:113   发布时间:2016-05-05 04:00:16.0
[本拉登]请进,多线程利用问题!!!!!!!!
本帖最后由 wangjun8868 于 2015-04-24 11:24:42 编辑
有一个数组,假设数组长度为100
线程个数为10个。
我想实现的情况是 第一个线程执行 数组0-9个,第二个线程执行10-19个,三个线程执行执行20-29个
依次类推,,这样能充分利用服务器CPU多核的利用率,但是这10个线程是一起执行的,各自执行自己的任务,互不干扰!
我是这么做的

  string[] tasks = new string[100];
        private void Form1_Load(object sender, EventArgs e)
        {
            for (int i = 0; i < 100; i++)
            {
                tasks[i] = "任务" + (i + 1);//加载模拟的任务 实际业务当中有很多代码 这里只是模拟
            }
        }
        //声明线程数组
        Thread[] workThreads = new Thread[10];

        public void DoWork(object ParObject)
        {
            int ThreadIndex = (int)ParObject;
            for (int i = ThreadIndex; i < ThreadIndex + 10; i++)
            {
                // 取i下表
                Console.WriteLine("线程" + workThreads[ThreadIndex].Name + "正在执行任务" + tasks[i]);//
                Thread.Sleep(10);
            }
            //如果此方法退出了,那么这个线程也就退出了
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            this.buttonStart.Enabled = false;
            //循环创建并启动线程执行
            for (int i = 0; i < workThreads.Length; i++)
            {
                if (workThreads[i] == null)
                {
                    //如果线程不存在,则创建
                    workThreads[i] = new Thread(new ParameterizedThreadStart(DoWork));
                    workThreads[i].Name = i.ToString();
                    workThreads[i].Start(i);
                }
                else
                {
                    //已经存在,如果没有运行,则启动
                    if (workThreads[i].ThreadState == ThreadState.Aborted || workThreads[i].ThreadState == ThreadState.Stopped)
                    {
                        workThreads[i] = new Thread(new ParameterizedThreadStart(DoWork));
                        workThreads[i].Name = i.ToString();
                        workThreads[i].Start(i);
                    }
                    else
                    {
                        workThreads[i].Start();
                    }
                }
            }
        }


线程0正在执行任务任务1
线程1正在执行任务任务2
线程2正在执行任务任务3
线程3正在执行任务任务4
线程4正在执行任务任务5
线程5正在执行任务任务6
线程6正在执行任务任务7
线程7正在执行任务任务8
线程8正在执行任务任务9
线程9正在执行任务任务10
线程1正在执行任务任务3
线程0正在执行任务任务2
线程3正在执行任务任务5
线程2正在执行任务任务4
线程4正在执行任务任务6
线程6正在执行任务任务8
线程5正在执行任务任务7
线程7正在执行任务任务9
线程8正在执行任务任务10
线程9正在执行任务任务11
线程1正在执行任务任务4
线程3正在执行任务任务6
线程0正在执行任务任务3
线程2正在执行任务任务5
线程4正在执行任务任务7
线程5正在执行任务任务8
线程6正在执行任务任务9
线程7正在执行任务任务10
线程8正在执行任务任务11
线程9正在执行任务任务12


但是实际打印的结果是 第二个线程执行了 0-9的某个任务 或者 50-59的某个任务,路乱了,
我的代码是不是有严重的逻辑错误?
------解决思路----------------------
要想按顺序输出,你的线程要加锁才行,否则顺序是随机的
------解决思路----------------------
唉,用不着写那么一堆有bug的代码。要比较“均衡地”10个线程执行任务,只需要这样写
string[] tasks = new string[100];
for (int i = 0; i < 100; i++)
{
    tasks[i] = "任务" + (i + 1);//加载模拟的任务 实际业务当中有很多代码 这里只是模拟
}
var q = from x in tasks.AsParallel().WithDegreeOfParallelism(10)
        select DoIt(x);
Console.WriteLine("总共用了{0}个线程执行了{1}个任务!", q.Distinct().Count(), tasks.Count());
static Random rnd = new Random();

static int DoIt(string task)
{
    var tid = Thread.CurrentThread.ManagedThreadId;
    Console.WriteLine("正在执行{0}_线程id={1}", task, tid);
    Thread.Sleep(rnd.Next(1000));
    Console.WriteLine("{0}结束", task);
    return tid;
}


只有一条 PLinq 语句就能写完了,何必整天纠结那些有 bug 的代码?
------解决思路----------------------
Parallel.For(0, 10, i =>
            {
            });

一句搞定……内部判断i的值,然后每个i对应不同的处理片段
------解决思路----------------------
想要“充分利用服务器CPU多核的利用率”为目标,你就不要想当然地让线程去“各自负责10个task”。显然有些线程处理了15个task,有些线程处理了5个task,也比“僵化分配”更有效率。

另外,多线程通常更重要的目的,不是什么“提高CPU多核的利用率”。因为即使单个CPU也并不是始终100%运行的,你的程序有大部分时间不占CPU时间(而是等待各类I/O操作)。如果说多线程是为了适配CPU多核,那么你可能通常只是启动2个线程就行了,启动10个线程就会被别人说三道四了。而实际上我们的程序在8核机器上并发使用30个线程,或者是你的电脑的windows系统整体来看有上千个线程,都跟你电脑有几个“核”根本无关。

你的一个程序说“在2核的机器上我就启动4个线程”,那么难道一个机器上就只有你这一个程序吗?肯定不是。这样去判断线程数,顶多是“看起来挺有道理”实际上不可靠。

在抢先式多任务windows系统下,合理的线程并发数量,取决于你的CPU是否有空闲(浪费的)时间,而不是取决于你的CPU核心数量。

所以如果你不确定“到底10线程还是50线程更合适”,那么应该使用 .net 的系统线程池。不要自己随便估计为10个线程。
------解决思路----------------------
引用:
想要“充分利用服务器CPU多核的利用率”为目标,你就不要想当然地让线程去“各自负责10个task”。显然有些线程处理了15个task,有些线程处理了5个task,也比“僵化分配”更有效率。

另外,多线程通常更重要的目的,不是什么“提高CPU多核的利用率”。因为即使单个CPU也并不是始终100%运行的,你的程序有大部分时间不占CPU时间(而是等待各类I/O操作)。如果说多线程是为了适配CPU多核,那么你可能通常只是启动2个线程就行了,启动10个线程就会被别人说三道四了。而实际上我们的程序在8核机器上并发使用30个线程,或者是你的电脑的windows系统整体来看有上千个线程,都跟你电脑有几个“核”根本无关。

你的一个程序说“在2核的机器上我就启动4个线程”,那么难道一个机器上就只有你这一个程序吗?肯定不是。这样去判断线程数,顶多是“看起来挺有道理”实际上不可靠。

在抢先式多任务windows系统下,合理的线程并发数量,取决于你的CPU是否有空闲(浪费的)时间,而不是取决于你的CPU核心数量。

所以如果你不确定“到底10线程还是50线程更合适”,那么应该使用 .net 的系统线程池。不要自己随便估计为10个线程。


诶我操,上次去面试就有个傻逼面试官跟我讲多线程是为了提高程序运行效率的,我听完这句话,我就什么都不想说了,只等他让我走了。
我觉得多线程通常用于阻塞的情况下,部分情况可以考虑利用多核的性能,现在C#本身就很多支持多线程运算的方法。
并不一定非要自己去手动去管理那些线程。

其实我就想问问你怎么知道CPU是否有空闲这个问题,如果提前判断,他可能后来又执行其他的程序,那么空闲又没了,
主要是系统又不是你一个程序独享,这点很困难吧?

------解决思路----------------------
学习了,多线程还是挺有意思的
------解决思路----------------------
for (int i = ThreadIndex*10; i < ThreadIndex * 10+10; i++)
            {
                // 取i下表
                Console.WriteLine("线程" + workThreads[ThreadIndex].Name + "正在执行任务" + tasks[i]);//
                Thread.Sleep(10);
            }
            //如果此方法退出了,那么这个线程也就退出了

------解决思路----------------------
利用多线程个编程。可以看看net4.0的并行编程内容 ,那里有详细说明。而且你写的代码里没有把执行顺序写出来。
------解决思路----------------------


  public class Consumer<T>
    {
        private int m_Count = 0;
        private Iterator<T>[] procHandlers;

        public void Init(int iProcesserNum, Action<T> callBack)
        {
            procHandlers = new Iterator<T>[iProcesserNum];
            for (int i = 0; i < iProcesserNum; i++)
            {
                procHandlers[i] = new Iterator<T>(callBack);
                procHandlers[i].Name = i.ToString();
                procHandlers[i].Start();
            }
        }

        public void Stop()
        {
            if (procHandlers != null)
            {
                for (int i = 0; i < procHandlers.Length; i++)
                {
                    procHandlers[i].Stop();
                }
            }
        }

        public void Enqueue(T t)
        {
            if (m_Count < 0) m_Count = 0;
            int index = (int)(m_Count % procHandlers.Length);
            procHandlers[index].Enqueue(t);
            m_Count++;
        }

        private class Iterator<TE>
        {
            private AutoResetEvent m_Signal = new AutoResetEvent(false);
            private Queue<TE> m_Queue = new Queue<TE>();
            private Thread thread;
            private Action<TE> m_CallBack;
            private bool isRunning;
            public string Name;
            public Iterator(Action<TE> callBack)
            {
                m_CallBack = callBack;

            }
            public void Start()
            {
                if (thread == null)
                {
                    isRunning = true;
                    thread = new Thread(new ThreadStart(SlicProc));
                    thread.Name = Name;
                    thread.IsBackground = true;
                    thread.Start();
                }
            }
            public void Stop()
            {

                isRunning = false;
                if (thread != null)
                {
                    try
                    {
                        thread.Abort();
                    }
                    finally
                    {

                    }
                }
                thread = null;
            }

            public void SetSignal()
            {
                m_Signal.Set();
            }


            public void Enqueue(TE te)
            {
                lock (m_Queue)
                {
                    m_Queue.Enqueue(te);
                }
                m_Signal.Set();
            }

            private TE Dequeue()
            {
                lock (m_Queue)
                {
                    if (m_Queue.Count > 0)
                    {
                        return m_Queue.Dequeue();
                    }
                }
                return default(TE);
            }


            internal void SlicProc()
            {
                while (m_Signal.WaitOne())
                {
                    while (m_Queue.Count > 0)
                    {
                        TE file = Dequeue();
                        if (file != null)
                        {
                            if (m_CallBack != null)
                            {
                                m_CallBack(file);
                            }
                        }
                    }
                    if (!isRunning)
                    {
                        break;
                    }
                }
            }
        }
    }


参考这个。这是把每个任务指定分配到哪个线程上跑
任务1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
线程1 2 3 4 5 1 2 3 4 5    1    2   3   4   5    1   2   3    4  5
这样    
------解决思路----------------------
这个是定义100个任务,分配给N个线程,只要线程有空闲就马上接收任何并干活。




  public class Worker
    {
        private Queue<string> queue = new Queue<string>();
        private object lockObj = new object();
        private AutoResetEvent[] m_Signals;
        private Thread[] threadList;
        private Action<string> _callBack;
        public Worker(Action<string> callBack)
        {
            _callBack = callBack;
        }


        public void Run(int threads, List<string> list)
        {
            queue.Clear();
            list.ForEach(o => queue.Enqueue(o));
            m_Signals = new AutoResetEvent[threads];
            threadList = new Thread[threads];
            for (int i = 0; i < threads; i++)
            {
                m_Signals[i] = new AutoResetEvent(false);
                threadList[i] = new Thread(new ParameterizedThreadStart(RunFunc));
                threadList[i].IsBackground = true;
                threadList[i].Name = i.ToString();
                threadList[i].Start(i);
            }
            WaitHandle.WaitAll(m_Signals);
        }

        private void RunFunc(object state)
        {
            int threadIndex = (int)state;
            while (queue.Count > 0)
            {
                lock (lockObj)
                {
                    if (queue.Count > 0)
                    {
                        string strRef = queue.Dequeue();
                        if (!string.IsNullOrEmpty(strRef))
                        {
                            if (_callBack != null)
                            {
                                _callBack(strRef);
                            }

                        }
                    }
                }
            }
            m_Signals[threadIndex].Set();

        }





    }
  相关解决方案