求一个多线程调用的写法,有100个任务,限定并发数为5

时间:2021-01-04 21:13:34
我现在有100个任务,需要多线程去完成,但是要限定同时并发数量不能超过5个。
请问我要怎么写?

26 个解决方案

#1


baidu,google一下线程同步,你能找到很多解决方案

最简单的办法是定义个静态全局变量当做红绿灯

线程开始时判断它是否大于4,不大于4就++并且执行代码,执行完毕--
如果大于4,做个while等待,等待时间可以是个随机数

#2


话说线程池可以
不过不建议用。

#3


4.0开始的Task处理这个问题很轻松

#4


楼上兄弟,这个貌似不需要用到线程同步的吧。
我自己写个简单的类就完成了需求了。
只是想使用系统的线程池来完成。

    public class LimitedTaskFactoryEx
    {
        private readonly int _maxThreadCount = 5;

        public LimitedTaskFactoryEx(int maxThreadCount)
        {
            _maxThreadCount = maxThreadCount;
            Start();
        }

        private readonly SafedQueue<Action> _actionQueue = new SafedQueue<Action>();

        public void StartNew(Action action)
        {
            _actionQueue.Enqueue(action);
        }

        private void Start()
        {
            for (int i = 0; i < _maxThreadCount; i++)
            {
                var thread = new Thread(new ThreadStart(InternalRun));
                thread.Start();
            }
        }

        private void InternalRun()
        {
            while (true)
            {
                var action = _actionQueue.Dequeue();
                if (action != null)
                {
                    action();
                }
                else
                {
                    Thread.Sleep(30);
                }
                Console.WriteLine(string.Format("Index: {0}, TaskId: {1}, ThreadId: {2}.", 1, Task.CurrentId,
        Thread.CurrentThread.ManagedThreadId));
            }
        }
    }


#5


2楼3楼,请给个用法,谢谢。

#6


求一个多线程调用的写法,有100个任务,限定并发数为5
任务全部添加进去
设置线程最大数  即可

#7


引用 5 楼 u012515900 的回复:
2楼3楼,请给个用法,谢谢。

早说嘛.
定义个线程池ThreadPool
然后可以指定最小线程数量和最大线程数量,你分别定义成0和5就行了
然后把方法用线程池执行
跟用线程差不多的.

不过只适用于线程间执行的方法互相没有依赖的情况
因为线程池具体如何调用线程的过程不受控制,执行顺序也不受控制.

这样其实就用不到你说的100个线程了.你那100个线程白定义了.

#8


是我不好,把100个任务看成100个线程同时只让5个执行,抱歉抱歉

#9


并发数不需要控制啊,你开几个线程就是并发几个,让线程自己取任务循环运行,注意任务列表的同步就行.

#10


线程池托管的,怎么就不建议用了
开5个线程去处理
volatile int number;
for(int i = 0; i< 5; i++)
{
       ThreadPool.QueueUserWorkItem(obj =>
       {
            while(number < 100)
            {
                  doSthing(number);
                  number++;
            }
       }
}

#11


恩,自己写的话需要考虑到性能的东西就比较多,所以我还是倾向用.net提供的方法 。
但是ThreadPool不能实例化,如果要设置最大线程数也是全局的,就会影响到其他地方。
另外,我发现设置ThreadPool的线程最大线程数貌似没用。

#12


开5个线程,每个线程每次只取一个任务进行处理,处理完毕累加计数器,接着取下一个任务,直到全部处理完

#13


呵呵,用线程池可以搞定了,我的最终办法出来了。
这样

            var workList = new SafedQueue<string>();
            for (int i = 0; i < 100; i++)
            {
                workList.Enqueue(i.ToString());
            }
            for(int i = 0; i< 5; i++)
            {
                ThreadPool.QueueUserWorkItem(obj =>
                {
                    while (true)
                    {
                        var item = workList.Dequeue();
                        if (item == null)
                        {
                            break;
                        }
                        Thread.Sleep(1000);
                        Console.WriteLine(item);
                    }
                });
            }

#14


貌似很简单的,我一开始想复杂了,想用TaskFactory去做。

#15


如果TaskFactory可以做到,你可以使用它。不过需要测试一下是否真的能够保证你选择并发数,以及性能。

如果要了解原理,自己可以写一个。但是这并不需要什么while循环,你更不能简单粗暴地把1000个任务“平均”分配给5个线程。应该让线程中的过程执行完毕之后,自己去取下一个任务。

例如可以这样
    public class MyTaskList
    {
        public List<Action> Tasks = new List<Action>();

        public void Start()
        {
            for (var i = 0; i < 5; i++)
                StartAsync();
        }

        public void StartAsync()
        {
            lock (Tasks)
            {
                if (Tasks.Count > 0)
                {
                    var t = Tasks[Tasks.Count - 1];
                    Tasks.Remove(t);
                    ThreadPool.QueueUserWorkItem(h =>
                    {
                        t();
                        StartAsync();
                    });
                }
            }
        }
    }


然后你可以使用一个console程序测试
            var rnd = new Random();
            var lst = new MyTaskList();
            for (var i = 0; i < 100; i++)
            {
                var s = rnd.Next(10);
                var j = i;
                var 测试任务 = new Action(() =>
                {
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经开始", j, s));
                    Thread.Sleep(s * 1000);
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经结束", j, s));
                });
                lst.Tasks.Add(测试任务);
            }
            lst.Start();
            Console.WriteLine("________________________End");


这里,自动加入的100个测试任务,每一个运行时间都是不定的、随机的,这是其一。

其二就是,这里的 StartAsync 方法瞬间就结束了,根本不会阻塞,也不会等待什么 while 循环结束。

#16


比如说你使用10个线程进行断点续传,也是这个业务逻辑。你用不着在线程中写什么while语句,如果写了while语句,那么一定很乱。

#17


可以重构一次,把“结束”的时序调整得更合理:

    public class MyTaskList
    {
        public List<Action> Tasks = new List<Action>();

        public void Start()
        {
            for (var i = 0; i < 5; i++)
                StartAsync();
        }

        public event Action Completed;

        public void StartAsync()
        {
            lock (Tasks)
            {
                if (Tasks.Count > 0)
                {
                    var t = Tasks[Tasks.Count - 1];
                    Tasks.Remove(t);
                    ThreadPool.QueueUserWorkItem(h =>
                    {
                        t();
                        StartAsync();
                    });
                }
                else if (Completed != null)
                    Completed();
            }
        }
    }


测试
            var rnd = new Random();
            var lst = new MyTaskList();
            for (var i = 0; i < 100; i++)
            {
                var s = rnd.Next(10);
                var j = i;
                var 测试任务 = new Action(() =>
                {
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经开始", j, s));
                    Thread.Sleep(s * 1000);
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经结束", j, s));
                });
                lst.Tasks.Add(测试任务);
            }
            lst.Completed += () => Console.WriteLine("____________________没有更多的任务了!");
            lst.Start();

#18


15\16\17楼 这位大牛  经常碰见。。。

#19


P哥难得一次不将大道理,而是实际性的给出解决方案。

#20


引用 19 楼 xgp0009 的回复:
P哥难得一次不将大道理,而是实际性的给出解决方案。

这话有点 求一个多线程调用的写法,有100个任务,限定并发数为5

#21


引用 7 楼 Z65443344 的回复:
Quote: 引用 5 楼 u012515900 的回复:

2楼3楼,请给个用法,谢谢。

早说嘛.
定义个线程池ThreadPool
然后可以指定最小线程数量和最大线程数量,你分别定义成0和5就行了
然后把方法用线程池执行
跟用线程差不多的.

不过只适用于线程间执行的方法互相没有依赖的情况
因为线程池具体如何调用线程的过程不受控制,执行顺序也不受控制.

这样其实就用不到你说的100个线程了.你那100个线程白定义了.


不要误导人家了!

线程池默认情况下,最小值是CPU的核心数,最大值则不确定,取决于系统和Framework版本

线程池不建议设置上下限,而且你还设置那么小,以为线程池是你专用的?

FCL各种异步、TASK、PLINQ都要用到线程池,最终会出现严重的“饥饿”现象。

#22


该回复于2015-02-28 22:08:00被版主删除

#23


引用 21 楼 lineages 的回复:
Quote: 引用 7 楼 Z65443344 的回复:

Quote: 引用 5 楼 u012515900 的回复:

2楼3楼,请给个用法,谢谢。

早说嘛.
定义个线程池ThreadPool
然后可以指定最小线程数量和最大线程数量,你分别定义成0和5就行了
然后把方法用线程池执行
跟用线程差不多的.

不过只适用于线程间执行的方法互相没有依赖的情况
因为线程池具体如何调用线程的过程不受控制,执行顺序也不受控制.

这样其实就用不到你说的100个线程了.你那100个线程白定义了.


不要误导人家了!

线程池默认情况下,最小值是CPU的核心数,最大值则不确定,取决于系统和Framework版本

线程池不建议设置上下限,而且你还设置那么小,以为线程池是你专用的?

FCL各种异步、TASK、PLINQ都要用到线程池,最终会出现严重的“饥饿”现象。

好吧我错了.楼主请无视.

#24


15楼老大很有耐心,还原写了实现代码,很感谢。
方案是OK的。
只是有一个小的疑问,如果任务比较多的话,这样的递规调用会不会导致堆栈太深,
C#我还没有学得那么深入,也不知道堆栈太深会不会引发什么问题。
有哪位明白的兄弟能否给解个惑?

#25


引用 24 楼 u012515900 的回复:
15楼老大很有耐心,还原写了实现代码,很感谢。
方案是OK的。
只是有一个小的疑问,如果任务比较多的话,这样的递规调用会不会导致堆栈太深,
C#我还没有学得那么深入,也不知道堆栈太深会不会引发什么问题。
有哪位明白的兄弟能否给解个惑?


   bu hui de !  da bu chu han zi

#26


用队列,启动5个线程去获取里面的值,执行

#1


baidu,google一下线程同步,你能找到很多解决方案

最简单的办法是定义个静态全局变量当做红绿灯

线程开始时判断它是否大于4,不大于4就++并且执行代码,执行完毕--
如果大于4,做个while等待,等待时间可以是个随机数

#2


话说线程池可以
不过不建议用。

#3


4.0开始的Task处理这个问题很轻松

#4


楼上兄弟,这个貌似不需要用到线程同步的吧。
我自己写个简单的类就完成了需求了。
只是想使用系统的线程池来完成。

    public class LimitedTaskFactoryEx
    {
        private readonly int _maxThreadCount = 5;

        public LimitedTaskFactoryEx(int maxThreadCount)
        {
            _maxThreadCount = maxThreadCount;
            Start();
        }

        private readonly SafedQueue<Action> _actionQueue = new SafedQueue<Action>();

        public void StartNew(Action action)
        {
            _actionQueue.Enqueue(action);
        }

        private void Start()
        {
            for (int i = 0; i < _maxThreadCount; i++)
            {
                var thread = new Thread(new ThreadStart(InternalRun));
                thread.Start();
            }
        }

        private void InternalRun()
        {
            while (true)
            {
                var action = _actionQueue.Dequeue();
                if (action != null)
                {
                    action();
                }
                else
                {
                    Thread.Sleep(30);
                }
                Console.WriteLine(string.Format("Index: {0}, TaskId: {1}, ThreadId: {2}.", 1, Task.CurrentId,
        Thread.CurrentThread.ManagedThreadId));
            }
        }
    }


#5


2楼3楼,请给个用法,谢谢。

#6


求一个多线程调用的写法,有100个任务,限定并发数为5
任务全部添加进去
设置线程最大数  即可

#7


引用 5 楼 u012515900 的回复:
2楼3楼,请给个用法,谢谢。

早说嘛.
定义个线程池ThreadPool
然后可以指定最小线程数量和最大线程数量,你分别定义成0和5就行了
然后把方法用线程池执行
跟用线程差不多的.

不过只适用于线程间执行的方法互相没有依赖的情况
因为线程池具体如何调用线程的过程不受控制,执行顺序也不受控制.

这样其实就用不到你说的100个线程了.你那100个线程白定义了.

#8


是我不好,把100个任务看成100个线程同时只让5个执行,抱歉抱歉

#9


并发数不需要控制啊,你开几个线程就是并发几个,让线程自己取任务循环运行,注意任务列表的同步就行.

#10


线程池托管的,怎么就不建议用了
开5个线程去处理
volatile int number;
for(int i = 0; i< 5; i++)
{
       ThreadPool.QueueUserWorkItem(obj =>
       {
            while(number < 100)
            {
                  doSthing(number);
                  number++;
            }
       }
}

#11


恩,自己写的话需要考虑到性能的东西就比较多,所以我还是倾向用.net提供的方法 。
但是ThreadPool不能实例化,如果要设置最大线程数也是全局的,就会影响到其他地方。
另外,我发现设置ThreadPool的线程最大线程数貌似没用。

#12


开5个线程,每个线程每次只取一个任务进行处理,处理完毕累加计数器,接着取下一个任务,直到全部处理完

#13


呵呵,用线程池可以搞定了,我的最终办法出来了。
这样

            var workList = new SafedQueue<string>();
            for (int i = 0; i < 100; i++)
            {
                workList.Enqueue(i.ToString());
            }
            for(int i = 0; i< 5; i++)
            {
                ThreadPool.QueueUserWorkItem(obj =>
                {
                    while (true)
                    {
                        var item = workList.Dequeue();
                        if (item == null)
                        {
                            break;
                        }
                        Thread.Sleep(1000);
                        Console.WriteLine(item);
                    }
                });
            }

#14


貌似很简单的,我一开始想复杂了,想用TaskFactory去做。

#15


如果TaskFactory可以做到,你可以使用它。不过需要测试一下是否真的能够保证你选择并发数,以及性能。

如果要了解原理,自己可以写一个。但是这并不需要什么while循环,你更不能简单粗暴地把1000个任务“平均”分配给5个线程。应该让线程中的过程执行完毕之后,自己去取下一个任务。

例如可以这样
    public class MyTaskList
    {
        public List<Action> Tasks = new List<Action>();

        public void Start()
        {
            for (var i = 0; i < 5; i++)
                StartAsync();
        }

        public void StartAsync()
        {
            lock (Tasks)
            {
                if (Tasks.Count > 0)
                {
                    var t = Tasks[Tasks.Count - 1];
                    Tasks.Remove(t);
                    ThreadPool.QueueUserWorkItem(h =>
                    {
                        t();
                        StartAsync();
                    });
                }
            }
        }
    }


然后你可以使用一个console程序测试
            var rnd = new Random();
            var lst = new MyTaskList();
            for (var i = 0; i < 100; i++)
            {
                var s = rnd.Next(10);
                var j = i;
                var 测试任务 = new Action(() =>
                {
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经开始", j, s));
                    Thread.Sleep(s * 1000);
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经结束", j, s));
                });
                lst.Tasks.Add(测试任务);
            }
            lst.Start();
            Console.WriteLine("________________________End");


这里,自动加入的100个测试任务,每一个运行时间都是不定的、随机的,这是其一。

其二就是,这里的 StartAsync 方法瞬间就结束了,根本不会阻塞,也不会等待什么 while 循环结束。

#16


比如说你使用10个线程进行断点续传,也是这个业务逻辑。你用不着在线程中写什么while语句,如果写了while语句,那么一定很乱。

#17


可以重构一次,把“结束”的时序调整得更合理:

    public class MyTaskList
    {
        public List<Action> Tasks = new List<Action>();

        public void Start()
        {
            for (var i = 0; i < 5; i++)
                StartAsync();
        }

        public event Action Completed;

        public void StartAsync()
        {
            lock (Tasks)
            {
                if (Tasks.Count > 0)
                {
                    var t = Tasks[Tasks.Count - 1];
                    Tasks.Remove(t);
                    ThreadPool.QueueUserWorkItem(h =>
                    {
                        t();
                        StartAsync();
                    });
                }
                else if (Completed != null)
                    Completed();
            }
        }
    }


测试
            var rnd = new Random();
            var lst = new MyTaskList();
            for (var i = 0; i < 100; i++)
            {
                var s = rnd.Next(10);
                var j = i;
                var 测试任务 = new Action(() =>
                {
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经开始", j, s));
                    Thread.Sleep(s * 1000);
                    Console.WriteLine(string.Format("第{0}个任务(用时{1}秒)已经结束", j, s));
                });
                lst.Tasks.Add(测试任务);
            }
            lst.Completed += () => Console.WriteLine("____________________没有更多的任务了!");
            lst.Start();

#18


15\16\17楼 这位大牛  经常碰见。。。

#19


P哥难得一次不将大道理,而是实际性的给出解决方案。

#20


引用 19 楼 xgp0009 的回复:
P哥难得一次不将大道理,而是实际性的给出解决方案。

这话有点 求一个多线程调用的写法,有100个任务,限定并发数为5

#21


引用 7 楼 Z65443344 的回复:
Quote: 引用 5 楼 u012515900 的回复:

2楼3楼,请给个用法,谢谢。

早说嘛.
定义个线程池ThreadPool
然后可以指定最小线程数量和最大线程数量,你分别定义成0和5就行了
然后把方法用线程池执行
跟用线程差不多的.

不过只适用于线程间执行的方法互相没有依赖的情况
因为线程池具体如何调用线程的过程不受控制,执行顺序也不受控制.

这样其实就用不到你说的100个线程了.你那100个线程白定义了.


不要误导人家了!

线程池默认情况下,最小值是CPU的核心数,最大值则不确定,取决于系统和Framework版本

线程池不建议设置上下限,而且你还设置那么小,以为线程池是你专用的?

FCL各种异步、TASK、PLINQ都要用到线程池,最终会出现严重的“饥饿”现象。

#22


该回复于2015-02-28 22:08:00被版主删除

#23


引用 21 楼 lineages 的回复:
Quote: 引用 7 楼 Z65443344 的回复:

Quote: 引用 5 楼 u012515900 的回复:

2楼3楼,请给个用法,谢谢。

早说嘛.
定义个线程池ThreadPool
然后可以指定最小线程数量和最大线程数量,你分别定义成0和5就行了
然后把方法用线程池执行
跟用线程差不多的.

不过只适用于线程间执行的方法互相没有依赖的情况
因为线程池具体如何调用线程的过程不受控制,执行顺序也不受控制.

这样其实就用不到你说的100个线程了.你那100个线程白定义了.


不要误导人家了!

线程池默认情况下,最小值是CPU的核心数,最大值则不确定,取决于系统和Framework版本

线程池不建议设置上下限,而且你还设置那么小,以为线程池是你专用的?

FCL各种异步、TASK、PLINQ都要用到线程池,最终会出现严重的“饥饿”现象。

好吧我错了.楼主请无视.

#24


15楼老大很有耐心,还原写了实现代码,很感谢。
方案是OK的。
只是有一个小的疑问,如果任务比较多的话,这样的递规调用会不会导致堆栈太深,
C#我还没有学得那么深入,也不知道堆栈太深会不会引发什么问题。
有哪位明白的兄弟能否给解个惑?

#25


引用 24 楼 u012515900 的回复:
15楼老大很有耐心,还原写了实现代码,很感谢。
方案是OK的。
只是有一个小的疑问,如果任务比较多的话,这样的递规调用会不会导致堆栈太深,
C#我还没有学得那么深入,也不知道堆栈太深会不会引发什么问题。
有哪位明白的兄弟能否给解个惑?


   bu hui de !  da bu chu han zi

#26


用队列,启动5个线程去获取里面的值,执行