当用户级线程阻塞时,关于阻塞"peer threads"的混淆



我读到了线程和进程之间的差异,在网上,有一个差异通常没有太多解释:

如果一个进程被阻止,其余进程可以继续执行。如果一个用户级线程被阻塞,那么它的所有对等线程也会被阻止。

这对我来说没有任何意义。如果调度程序不能在阻塞的线程和就绪/可运行的线程之间切换,那么并发的意义是什么。给出的原因是,由于操作系统不会区分给定父进程的各个线程,因此它会同时阻止所有线程。

我觉得这很难令人信服,因为所有现代操作系统都有带有线程ID的线程控制块,即使它只在父进程的内存空间内有效。就像Galvin的《操作系统》一书中给出的例子一样,如果拼写检查线程无法连接到某个在线词典,我不希望处理我打字的线程被阻止。

要么我对这个概念的理解是错误的,要么这些网站多年来只是复制了一些旧的线程差异。此外,我在书中找不到这种说法,比如Galvin的书,或者在William Stalling的COA书中,其中讨论了线程。

这些是我发现声明的地方:

  • https://www.geeksforgeeks.org/difference-between-process-and-thread/
  • https://www.tutorialspoint.com/difference-between-process-and-thread
  • https://www.guru99.com/difference-between-process-and-thread.html
  • https://www.javatpoint.com/process-vs-thread

内核级和用户级线程之间存在差异。简而言之:

  • 内核级线程:由操作系统管理的线程,包括调度。它们是在处理器上执行的内容。这可能是我们大多数人对线程的看法
  • 用户级线程:由程序本身管理的线程。在某些情况下,它们也被称为纤维或协程。与内核级线程相比;"产生执行";,即从一个用户级别切换到另一个用户级线程是由程序明确地完成的用户级线程映射到内核级线程

由于用户级线程需要映射到内核级线程,因此需要选择合适的映射。您可以将每个用户级别映射到一个单独的内核级别线程。您还可以将多个用户级别映射到一个内核级别线程。在后一种映射中,您让多个并发执行路径由一个线程"执行;正如我们所知";。如果其中一条路径阻塞,请记住用户级线程需要产生执行,那么执行(内核级)线程就会阻塞,这会导致所有其他分配的路径也被有效地阻塞。我想,这就是语句所指的。FYI:在Java中,用户级线程——你在程序中执行的多线程——由JVM(即运行时系统)映射到内核级线程。


相关内容:

  • 了解java的本机线程和jvm
  • 线和纤维有什么区别
  • 用户空间和内核空间之间的区别是什么
  • 并发编程和并行编程之间的区别是什么
  • 实现线程

至少在Java的早期,用户级线程被称为"绿色线";,在不支持本机线程的操作系统上实现Java线程。还有一篇Wiki文章https://en.wikipedia.org/wiki/Green_threads解释了它的由来和意义。

(这要追溯到台式机/笔记本电脑是单处理器系统的时候,它们的1个物理插槽中有一个单核CPU,SMP机器大多只作为多插槽存在。)

你是对的,这太可怕了,一旦主流操作系统成长为支持本地线程,人们大多就不再这样做了。至少对Java来说,绿色线程指的是编程语言Java的原始线程库的名称(它在1.1版本中发布,然后绿色线程在1.3版本中被放弃为本机线程)

因此,如果您不希望拼写检查线程阻塞整个应用程序,请使用Java 1.3或更高版本P这是古代历史。

虽然当系统调用返回它将被阻塞时,使用非阻塞IO和上下文切换有一定的空间,但通常最好让内核处理线程阻塞和取消阻塞,所以这就是正常的现代系统所做的


IIRC在Solaris上也使用了N:M模型,其中N个用户空间线程可能由少于N个的内核线程处理。这可能意味着;"对等";线程(共享同一内核线程)就像你引用的那样,而不是完全糟糕的纯用户空间绿色线程。

(即,只有部分线程共享同一内核线程。)

Linux上的pthreads使用1:1模型,其中每个软件线程都是由内核调度的单独任务。

谷歌找到https://flylib.com/books/en/3.19.1.51/1/它定义了这些线程模型,并对它们进行了一些讨论,包括N:M混合模型和N:1用户空间(也称为绿色线程)模型,如果它想避免阻塞其他线程,则需要使用非阻塞I/O。(例如,如果系统调用返回EAGAIN,或者在异步读写排队后,进行用户空间上下文切换。)

好的,其他答案提供了详细信息。

但要打你的主要对流在中间:

  • 这篇文章有点错误,缺乏必要的上下文(请参阅@akuzminykh对用户级线程和内核级线程的解释中的所有细节)
  • 这对Java程序员意味着什么:不要为这些解释而烦恼。如果你的一个Java线程阻塞(由于I/O等原因),这将不会对你的任何其他线程产生影响(当然,除非你明确希望它们阻塞,但你必须明确使用机制)

Java中线程是如何被阻塞的?

  • 如果调用sleep()wait()等,则当前执行该代码的线程(而不是调用它们的对象)将被阻止。这些将在某些事件中释放:一旦定时器用完或线程被另一个线程中断,睡眠将结束,一旦另一个进程通知它,等待将释放
  • 如果遇到synchronized(lockObj)块或方法:一旦占用lockObj的另一个线程释放它,就会释放
    • 与此密切相关的是,如果您输入ThreadGates、互斥等,那么所有1000个用于扩展线程控制(如会合等)的专用类
  • 如果您调用块I/O方法,例如从InputStream等进行块读取:int amountOfBytesRead = read(buffer, offset, length)String line = myBufferedReader.readLine();
    • 与此相反,有许多非阻塞I/O操作,如java.nio(非阻塞I/O)包中的大多数操作,它们会立即返回,但可能指示无效的结果值
  • 如果垃圾回收器进行了一个快速清理周期(通常很短,你甚至不会注意到,线程会再次自动释放)
  • 如果您为流上的某些持久lambda函数(如myList.parallelStream().forEach(myConsumerAction))调用.parallelStream()函数,这些函数如果太复杂或元素太多,将由自动多线程机制处理(您不会注意到,因为在完成所有工作后,您的调用线程将正常恢复,就像调用了普通方法一样)。点击此处查看更多信息:https://www.baeldung.com/java-when-to-use-parallel-stream

相关内容

  • 没有找到相关文章

最新更新