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

编程有钱人了 2015-04-24 11:23:28
有一个数组,假设数组长度为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的某个任务,路乱了,
我的代码是不是有严重的逻辑错误?
...全文
519 44 打赏 收藏 转发到动态 举报
写回复
用AI写文章
44 条回复
切换为时间正序
请发表友善的回复…
发表回复
编程有钱人了 2015-04-27
  • 打赏
  • 举报
回复
LS几个说的都很有道理,学习了!
  • 打赏
  • 举报
回复
其实折腾了一大圈而写出一堆有bug的代码,不如先学好“一句话”的多线程编程技术。 不需要自己写 new Thread() 语句等等底层的东西,而要学会利用到 .net framework 已经写好的线程管理框架。
  • 打赏
  • 举报
回复
引用 23 楼 MSTOP 的回复:
如果仅仅是处理.用 Parallel.For 就可以. 如果想有序.那还得分配一个与结果相同长度的数组.每个ID写入相应ID号的下标. 如果想更复杂的处理.那只能是开线程或线程池.最后还要在主函数中轮询线程标志(如计数器)是否执行完毕.然后统一处理结果.
这还是重复 lz 的“我想实现的情况是 第一个线程执行 数组0-9个,第二个线程执行10-19个,三个线程执行执行20-29个”的话。而且lz已经在代码中写 new Thread() 语句了。 lz 在问题一开始就纠结于这个程序设计“有bug”的问题。
puler 2015-04-26
  • 打赏
  • 举报
回复
Parallel.For
引用 23 楼 MSTOP 的回复:
如果仅仅是处理.用 Parallel.For 就可以. 如果想有序.那还得分配一个与结果相同长度的数组.每个ID写入相应ID号的下标. 如果想更复杂的处理.那只能是开线程或线程池.最后还要在主函数中轮询线程标志(如计数器)是否执行完毕.然后统一处理结果.
Parallel.For 不是很适用,想在并行计算中,对线程进行小幅控制,又需要结果较为精确的时候,效率并不是很理想 特别是For循环过程中还反复调用其他方法与返回值
  • 打赏
  • 举报
回复
或者如果一定要写成 new Thread() 语句,那么就是
static string[] tasks;
static int index;

static void DoIt()
{
    for (; ; )
    {
        string task;
        lock (tasks)
        {
            if (index >= tasks.Length)      //所有任务都完成了
                return;

            task = tasks[index++];
        }
        var tid = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine("正在执行{0}_线程id={1}", task, tid);
        Thread.Sleep(rnd.Next(5000));     //随机阻塞一会儿
        Console.WriteLine("{0}结束", task);
    }
}

static Random rnd = new Random();
tasks = new string[100];
for (int i = 0; i < 100; i++)
{
    tasks[i] = "任务" + (i + 1);//加载模拟的任务 实际业务当中有很多代码 这里只是模拟
}
for (var i = 0; i < 10; i++)
    new Thread(h => DoIt()).Start();
基本上,看看它的运行结果就知道,一个任务可以“提前”在哪个线程上执行,不能想当然地预先分配,而应该在运行中自动却分配。 这是算法本身的重点。
  • 打赏
  • 举报
回复
要使用多个线程去处理一个数组,如果一定要自己写,并且使用系统线程池,可以这样写
static string[] tasks;
static int index;

static void DoIt()
{
begin:
    string task;
    lock (tasks)
    {
        if (index >= tasks.Length)      //所有任务都完成了
            return;

        task = tasks[index++];
    }
    var tid = Thread.CurrentThread.ManagedThreadId;
    Console.WriteLine("正在执行{0}_线程id={1}", task, tid);
    Thread.Sleep(rnd.Next(5000));     //随机阻塞一会儿
    Console.WriteLine("{0}结束", task);
    goto begin;
}

static Random rnd = new Random();
调用它是
tasks = new string[100];
for (int i = 0; i < 100; i++)
    tasks[i] = "任务" + (i + 1);//加载模拟的任务 实际业务当中有很多代码 这里只是模拟
for (var i = 0; i < 10; i++)
    ThreadPool.QueueUserWorkItem(h => DoIt());
这里唯一的技术就在于:使用 lock 来进行线程同步,仅需要同步 index++ 这一条语句而已!! 不过这种代码写的也太多了,如果你需要写大量这类代码(而不是偶尔写一个小练习),这种代码太丑、太繁琐、太不优雅了。
华芸智森 2015-04-26
  • 打赏
  • 举报
回复
楼主这东西,我不知道是不是你软件功能中的一个很重要很重要的部分,值得你花这么大的力气. 简单写就一句话搞定. 类似的功能,如果是复杂的写.大概要一千行左右.
本拉灯 2015-04-26
  • 打赏
  • 举报
回复
引用 17 楼 wangjun8868 的回复:
[quote=引用 15 楼 wyd1520 的回复:] 这个是定义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();

        }





    }

   List<string> lst = new List<string>();
            Action<string> action = DoWork2;
            for (int i = 0; i < 100; i++)
            {
                lst.Add("任务" + (i + 1));
            }
            Worker work = new Worker(action);
            work.Run(10,lst);
报错啊拉登 “System.NotSupportedException”类型的未经处理的异常在 mscorlib.dll 中发生 其他信息: 不支持一个 STA 线程上针对多个句柄的 WaitAll。[/quote] 。。。你这东西用在哪个地方了?
华芸智森 2015-04-26
  • 打赏
  • 举报
回复
        Parallel.For(0,          '//数组开始的编号.
                     ThMax,      '//最大线程数.
                     Sub(StepId) '//步长
                         For I As Integer = StepId To Files.Length - 1 Step ThMax
                                 .....
                          Next
                      )   
华芸智森 2015-04-26
  • 打赏
  • 举报
回复
如果仅仅是处理.用 Parallel.For 就可以. 如果想有序.那还得分配一个与结果相同长度的数组.每个ID写入相应ID号的下标. 如果想更复杂的处理.那只能是开线程或线程池.最后还要在主函数中轮询线程标志(如计数器)是否执行完毕.然后统一处理结果.
华芸智森 2015-04-26
  • 打赏
  • 举报
回复
Parallel.For
puler 2015-04-25
  • 打赏
  • 举报
回复
线程池没试过,说是不适合长时间进行持续计算的线程
  • 打赏
  • 举报
回复
总之有一个原则,作为一个练习而“玩儿玩儿”可以,但是如果你实际上是自己写一个线程池机制,还是要充分利用.net framework已经提供的系统线程池或者并行Task编程机制,不要自己从底层 Thread 去写。有一些禁忌你没有考虑到。
  • 打赏
  • 举报
回复
引用 6 楼 sp1234 的回复:
想要“充分利用服务器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个线程。
哇,你也太厉害了吧
  • 打赏
  • 举报
回复
引用 12 楼 wangjun8868 的回复:
其实我也不想手动分配,但是我怎么知道哪个线程是闲(如果闲着就让他去干活,而不是僵化的分配),我的代码只是为了暂时解决目前的问题,我也知道这样做不是非常合理
每一个当前有空闲的处理线程从队列“取下”任务,这自然是更“合理”一些的分配方法。这应该是很容易想到的。 不过,.net在5年前就已经发布有“一句话”就能搞定的并行编程机制。对于编程开发人员,应该更熟悉那些“新”技术,而不是最初级的概念。 另外要说明一下的是,不论是Task还是ThreadPool,在处理多线程并发时都有一个基本的做法,当你同时注册10个线程时,它只是启动很少任务而已(根据cpu数量而定),然后过了大概1秒钟才启动更多的线程。绝不是一下子就启动多个线程的!从这一点也可以看出那些设计.net framework和windows的编程人员是经过测算和分析的。
编程有钱人了 2015-04-25
  • 打赏
  • 举报
回复
引用 15 楼 wyd1520 的回复:
这个是定义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();

        }





    }

   List<string> lst = new List<string>();
            Action<string> action = DoWork2;
            for (int i = 0; i < 100; i++)
            {
                lst.Add("任务" + (i + 1));
            }
            Worker work = new Worker(action);
            work.Run(10,lst);
报错啊拉登 “System.NotSupportedException”类型的未经处理的异常在 mscorlib.dll 中发生 其他信息: 不支持一个 STA 线程上针对多个句柄的 WaitAll。
编程有钱人了 2015-04-25
  • 打赏
  • 举报
回复
引用 15 楼 wyd1520 的回复:
这个是定义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();

        }





    }
还是拉登做的好,是我想要的
  • 打赏
  • 举报
回复
Parallel.For(0, 10, i =>
            {
            });
一句搞定……内部判断i的值,然后每个i对应不同的处理片段
  • 打赏
  • 举报
回复
唉,用不着写那么一堆有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 的代码?
编程有钱人了 2015-04-24
  • 打赏
  • 举报
回复
引用 1 楼 bdmh 的回复:
要想按顺序输出,你的线程要加锁才行,否则顺序是随机的
加了锁这个10个线程能同时运行吗?
加载更多回复(12)

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧