c# – 理解Parallel.Invoke,创建和重用线程
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c# – 理解Parallel.Invoke,创建和重用线程,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5060字,纯文字阅读大概需要8分钟。
内容图文
![c# – 理解Parallel.Invoke,创建和重用线程](/upload/InfoBanner/zyjiaocheng/789/4def904d19ec48e88b7d811d2a9f10b1.jpg)
我试图了解Parallel.Invoke如何创建和重用线程.
我运行了以下示例代码(来自MSDN,https://msdn.microsoft.com/en-us/library/dd642243(v=vs.110).aspx):
using System;
using System.Threading;
using System.Threading.Tasks;
class ThreadLocalDemo
{
static void Main()
{
// Thread-Local variable that yields a name for a thread
ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
{
return "Thread" + Thread.CurrentThread.ManagedThreadId;
});
// Action that prints out ThreadName for the current thread
Action action = () =>
{
// If ThreadName.IsValueCreated is true, it means that we are not the
// first action to run on this thread.
bool repeat = ThreadName.IsValueCreated;
Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
};
// Launch eight of them. On 4 cores or less, you should see some repeat ThreadNames
Parallel.Invoke(action, action, action, action, action, action, action, action);
// Dispose when you are done
ThreadName.Dispose();
}
}
据我所知,Parallel.Invoke尝试在这里创建8个线程 – 每个动作一个.所以它创建了第一个线程,运行第一个动作,然后通过它给线程一个ThreadName.然后它创建下一个线程(获取不同的ThreadName),依此类推.
如果它无法创建新线程,它将重用之前创建的其中一个线程.在这种情况下,repeat的值将为true,我们可以在控制台输出中看到这一点.
这是正确的,直到这里?
第二个评论(“启动其中的八个.在4核或更少,您应该看到一些重复的ThreadNames”)意味着Invoke创建的线程对应于处理器的可用cpu线程:在4个核心上我们有8个CPU线程,至少有一个忙(运行操作系统和东西),所以Invoke只能使用7个不同的线程,所以我们必须至少得到一个“重复”.
我对这个评论的解释是否正确?
我在我的PC上运行了这个代码,它有一个Intel®Core?i7-2860QM处理器(即4个内核,8个cpu线程).我期望得到至少一个“重复”,但我没有.当我将Invoke更改为10而不是8个动作时,我得到了这个输出:
ThreadName = Thread6
ThreadName = Thread8
ThreadName = Thread6 (repeat)
ThreadName = Thread5
ThreadName = Thread3
ThreadName = Thread1
ThreadName = Thread10
ThreadName = Thread7
ThreadName = Thread4
ThreadName = Thread9
所以我在控制台应用程序中至少有9个不同的线程.这与我的处理器只有8个线程的事实相矛盾.
所以我猜我的一些推理是错误的. Parallel.Invoke的工作方式与我上面描述的不同吗?如果有,怎么样?
解决方法:
如果你将少于10个项目传递给Parallel.Invoke,并且你没有在选项中指定MaxDegreeOfParallelism(所以 – 你的情况),它将在线程池sheduler上使用rougly以下代码并行运行它们:
var actions = new [] { action, action, action, action, action, action, action, action };
var tasks = new Task[actions.Length];
for (int index = 1; index < tasks.Length; ++index)
tasks[index] = Task.Factory.StartNew(actions[index]);
tasks[0] = new Task(actions[0]);
tasks[0].RunSynchronously();
Task.WaitAll(tasks);
所以只需要一个常规的Task.Factory.StartNew.如果您将查看线程池中的最大线程数
int th, io;
ThreadPool.GetMaxThreads(out th, out io);
Console.WriteLine(th);
您将看到一些大数字,例如32767.因此,将执行Parallel.Invoke的线程数(在您的情况下)不限于cpu核心的数量.即使在单核cpu上,它也可以并行运行8个线程.
您可能会想,为什么有些线程会被重用?因为在线程池线程上完成工作时 – 该线程返回到池并准备接受新工作.你的例子中的动作基本上根本不起作用,并且完成得非常快.所以有时第一个线程是通过Task.Factory.StartNew启动的,它已经完成了你的操作并在所有后续线程启动之前返回到池中.这样线程就可以重用了.
顺便说一句,你可以在你的例子中看到(重复)8个动作,如果你努力的话,你可以在8个核心(16个逻辑核心)处理器上看到7个动作.
更新以回答您的评论.线程池调度程序不必立即创建新线程.线程池中有最小和最大线程数.如何看我上面已经显示的最大值.要查看最小数量:
int th, io;
ThreadPool.GetMinThreads(out th, out io);
该数字通常等于核心数(例如8).现在,当您请求在线程池线程上执行新操作,并且线程池中的线程数小于最小值时 – 将立即创建新线程.但是,如果可用线程的数量大于最小值 – 在创建新线程之前将引入某些延迟(我不记得确切地说有多长时间,大约500ms).
您在评论中添加的声明我非常怀疑可以在2-3秒内执行.对我来说,它最多执行0.3秒.因此,当线程池创建前8个线程时,在创建第9个之前有500毫秒的延迟.在该延迟期间,前8个线程中的一些(或全部)完成了它们的工作并且可用于新工作,因此不需要创建新线程并且可以重用它们.
要验证这一点,请引入更大的延迟:
static void Main()
{
// Thread-Local variable that yields a name for a thread
ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
{
return "Thread" + Thread.CurrentThread.ManagedThreadId;
});
// Action that prints out ThreadName for the current thread
Action action = () =>
{
// If ThreadName.IsValueCreated is true, it means that we are not the
// first action to run on this thread.
bool repeat = ThreadName.IsValueCreated;
Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
Thread.Sleep(1000000);
};
int th, io;
ThreadPool.GetMinThreads(out th, out io);
Console.WriteLine("cpu:" + Environment.ProcessorCount);
Console.WriteLine(th);
Parallel.Invoke(Enumerable.Repeat(action, 100).ToArray());
// Dispose when you are done
ThreadName.Dispose();
Console.ReadKey();
}
您将看到现在线程池必须每次都创建新线程(远远超过核心),因为它们在繁忙时无法重用先前的线程.
您还可以增加线程池中的最小线程数,如下所示:
int th, io;
ThreadPool.GetMinThreads(out th, out io);
ThreadPool.SetMinThreads(100, io);
这将消除延迟(直到创建100个线程),在上面的例子中你会注意到.
内容总结
以上是互联网集市为您收集整理的c# – 理解Parallel.Invoke,创建和重用线程全部内容,希望文章能够帮你解决c# – 理解Parallel.Invoke,创建和重用线程所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。