在C 4.0中,在system.threading.tasks名称空间中有Task。Thread和Task的真正区别是什么?我做了一些示例程序(从msdn获得的帮助),以便自己学习
1 2 3
| Parallel.Invoke
Parallel.For
Parallel.ForEach |
但是有很多疑问,因为这个想法不太清楚。
我最初在StackOverflow中搜索过类似类型的问题,但可能有这个问题标题,我无法得到相同的问题。如果有人知道之前在这里发布的相同类型的问题,请提供链接的参考。
在计算机科学术语中,Task是未来或承诺。(有些人简单地使用这两个术语,有些人使用不同的术语,没有人能同意一个精确的定义。)基本上,一个Task的"承诺"会给你一个T,但现在不行,亲爱的,我有点忙,你为什么不晚点回来呢?
Thread是履行承诺的一种方式。但并非所有的Task都需要一个全新的Thread。(事实上,创建线程通常是不可取的,因为这样做比从线程池重新使用现有线程要昂贵得多。如果您等待的值来自文件系统、数据库或网络,那么当线程可以服务于其他请求时,就不需要等待线程等待数据。相反,Task可能注册回调,以便在值准备就绪时接收值。
特别是,Task没有说明为什么返回值需要这么长时间。可能是计算需要很长时间,也可能是提取需要很长时间。只有在前一种情况下,才能使用Thread来运行Task。(在.NET中,线程非常昂贵,因此您通常希望尽可能避免线程,并且仅在希望在多个CPU上运行多个繁重计算时才使用线程。例如,在Windows中,线程的重量为12 kibyte(我认为),在Linux中,线程的重量只有4 kibyte,在erlang/beam中甚至只有400 byte。在.NET中,它是1 mibyte!)
- 有趣的是,在TPL(任务并行库)的早期预览版本中,有任务和未来。Future被重命名为task。:)
- 你是如何为.NET计算1MB的?
- @Danvallejo:在对TPL设计团队的采访中提到了这个数字。我不能告诉你是谁说的或者是哪个采访,我几年前看过。
- "不是每个任务都需要一个线程"-我不是一个.NET用户。对这一说法有疑问。要从文件系统或数据库中获取数据,我们需要执行API。而线程是实现这一点的唯一工具。不是吗?
- @当然,但它不需要是另一个线程,它可以是首先请求工作的线程。
- 既然你想回电话,你能解释一下吗?
- 关于"不是每个任务都需要一个线程",这对我来说没有意义。所有任务都在一些线程的上下文中运行,包括回调。
- @Rohitsharma,我认为他的意思是"不是每个任务都需要一个单独的线程"。
- @Danvallejo尺寸在Navan发布的第二段视频的开头提到。
- @当您的意思是"准备就绪"时,这是否意味着调度程序准备就绪?
- 因此,默认情况下,任务不会分配给新线程。你需要明确地做到这一点。对吗?
- 线程的默认堆栈保留大小为1MB,如此处所述:msdn.microsoft.com/en-us/library/windows/desktop/…
- .NET只在Windows上使用Windows线程,因此大小是相同的-默认情况下,两个线程的虚拟内存通常为1 mib。物理内存根据需要用于页面大小的块(通常为64 kib),与本机代码相同。例如,最小线程堆栈大小取决于Vista的OS-256 kib。在x86 Linux上,默认值通常是2 mib——同样,以页面大小的块分配。(简化)Erlang每个进程只使用一个系统线程,这400个字节表示类似于.nets Task的内容。
- @Rohisharma Task.FromResult("Hi!")使用的是什么线程?
- "基本上,任务承诺"会给你一个T,但现在不行,亲爱的,我有点忙,你为什么不晚点回来?"=>我发现的最佳定义。
任务是你想要完成的事情。
线程是执行该任务的众多可能的工作线程之一。
在.NET 4.0术语中,任务表示异步操作。线程用于完成该操作,方法是将工作拆分为块并分配给单独的线程。
螺纹
对于裸机,您可能不需要使用它,您可能可以使用LongRunning任务,并从.NET Framework 4(2002年2月)及更高版本(也是.NET核心)中包含的TPL-任务并行库中获益。
任务
线程之上的抽象。它使用线程池(除非您将任务指定为LongRunning操作,否则,会在引擎盖下为您创建一个新线程)。
线程池
顾名思义:线程池。.NET框架是否为您处理有限数量的线程。为什么?因为在只有8核的处理器上打开100个线程来执行昂贵的CPU操作绝对不是一个好主意。框架将为您维护这个池,重用线程(不在每个操作中创建/杀死线程),并并行执行其中的一些线程,这样您的CPU就不会烧坏。
好的,但是什么时候使用每一个呢?
在简历中:总是使用任务。
任务是一个抽象的概念,所以使用起来容易得多。我建议你总是尝试使用任务,如果你面临一些问题,使你需要自己处理一个线程(可能1%的时间),然后使用线程。
但请注意:
- I/O绑定:对于I/O绑定操作(数据库调用、读/写文件、API调用等),避免使用常规任务,使用LongRunning任务(或线程,如果需要的话)。因为使用任务会导致一个线程池,其中有几个线程正忙,还有许多其他任务等待轮到它使用该池。
- CPU绑定:对于CPU绑定的操作,只需使用正常的任务(内部将使用线程池)就可以了。
您可以使用Task指定要执行的操作,然后用Thread附加该Task。这样,Task将在新生成的Thread中执行,而不是在GUI线程上执行。
使用Task和TaskFactory.StartNew(Action action)。在这里,您执行一个委托,所以如果您没有使用任何线程,它将在同一个线程(GUI线程)中执行。如果您提到一个线程,您可以在另一个线程中执行这个Task。这是一项不必要的工作,因为您可以直接执行委托或将该委托附加到一个线程并在该线程中执行该委托。所以不要用它。这是不必要的。如果你打算优化你的软件,这是一个很好的候选者要删除。
**请注意,Action是delegate。
除上述几点外,我们很高兴知道:
默认情况下,任务是后台任务。不能有前台任务。另一方面,线程可以是后台或前台(使用isbackground属性更改行为)。
在线程池中创建的任务回收有助于保存资源的线程。所以在大多数情况下,任务应该是您的默认选择。
如果操作很快,最好使用任务而不是线程。对于长时间运行的操作,任务没有提供比线程更多的优势。
我通常使用Task与winforms和简单的后台工作人员进行交互,使其不冻结用户界面。这里有一个我喜欢使用Task的例子
1 2 3 4 5 6 7 8 9 10 11
| private async void buttonDownload_Click (object sender, EventArgs e )
{
buttonDownload .Enabled = false;
await Task .Run(() => {
using (var client = new WebClient ())
{
client .DownloadFile("http://example.com/file.mpeg", "file.mpeg");
}
})
buttonDownload .Enabled = true;
} |
VS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private void buttonDownload_Click (object sender, EventArgs e )
{
buttonDownload .Enabled = false;
Thread t = new Thread (() =>
{
using (var client = new WebClient ())
{
client .DownloadFile("http://example.com/file.mpeg", "file.mpeg");
}
this.Invoke((MethodInvoker )delegate()
{
buttonDownload .Enabled = true;
});
});
t .IsBackground = true;
t .Start();
} |
区别在于您不需要使用MethodInvoker和更短的代码。
任务就像一个您想要执行的操作,线程通过多个进程节点帮助管理这些操作。任务是一个轻量级选项,因为线程可能导致复杂的代码管理我建议阅读"永远都是世界上最好的"任务。
螺纹