【问题标题】:Confusion regarding the Blocking of "peer threads" when a user-level thread blocks当用户级线程阻塞时,关于阻塞“对等线程”的困惑
【发布时间】:2021-10-29 01:13:42
【问题描述】:

我正在阅读有关线程和进程之间的差异的信息,实际上在网上到处都是,通常写一个差异而没有太多解释:

如果一个进程被阻塞,剩余的进程可以继续执行。 如果一个用户级线程被阻塞,它的所有对等线程也会得到 被屏蔽了。

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

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

要么我对这个概念的理解有误,要么所有这些网站多年来都只是复制了一些旧线程差异。此外,我无法在诸如 Galvin 的书籍或 William Stalling 的 COA 书中讨论过线程的书籍中找到此声明。

这些是我找到这些语句的资源:

【问题讨论】:

  • 请提供来源(f)或更多上下文...因为可能来源错误,或者更可能的是,该段落的上下文改变了含义。也许这一段都是关于控制一组线程的?
  • 线程和进程的主要区别在于前者共享一个地址空间,而后者不共享。事实上,大多数操作系统调度线程,进程实体只是每个线程的一个属性。
  • 您引用的文字似乎存在here。始终对 Medium 上的内容持保留态度。他们的信息质量是相当规律的,在这种情况下,完全是错误的。
  • @AMANKUMAR:这些都是用户提交的教程,经常重复有人在某处读过但不完全理解的语句。或者没有意识到已经过时,或者他们没有考虑到现实世界的现代系统是否真的做到了这一点。
  • 问题是,写一个好的教程是很多工作。这些网站上的许多帖子(我认为)都是由刚刚学习某个主题的初学者作为学习练习编写的。 Stack Overflow 也是如此,因为 cmets 更显眼,专家评审更积极,并且更期望通过编辑来修复错误。此外,SO 答案更短,因此任何错误通常都更关注答案的重点(尽管我们确实看到答案因其主要观点而获得赞成票,而它们包含对其他事物的错误陈述)。

标签: java multithreading operating-system cpu-architecture green-threads


【解决方案1】:

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

但是要在中间击中你的主要convern:

  • 这篇文章有点错误,缺少必要的上下文(请参阅 @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)包,它们会立即返回,但可能指示无效的结果值
  • 如果垃圾收集器执行快速清理周期(通常很短,您甚至不会注意到,并且线程会再次自动释放)
  • 如果您调用.parallelStream() 函数用于流上的某些持久 lambda 函数(如 myList.parallelStream().forEach(myConsumerAction)),如果太复杂或包含太多元素,则由自动多线程机制处理(您不会注意到,因为完成所有工作后,您的调用线程将正常恢复,就像调用正常方法一样)。在此处查看更多信息:https://www.baeldung.com/java-when-to-use-parallel-stream

【讨论】:

    【解决方案2】:

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

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

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

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

    虽然在系统调用返回它会阻塞时使用非阻塞 IO 和上下文切换有一定的范围,但通常最好让内核处理线程阻塞和解除阻塞,这就是普通现代系统所做的。


    在 Solaris 上的 IIRC 还使用了 N:M 模型,其中 N 个用户空间线程可能由少于 N 个内核线程处理。这可能意味着在您的引用中拥有一些“对等”线程(共享相同的内核线程),而不是完全可怕的纯用户空间绿色线程。

    (即只有 一些 个线程共享同一个内核线程。)

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

    Google 发现 https://flylib.com/books/en/3.19.1.51/1/ 定义了这些线程模型并对其进行了一些讨论,包括 N:M 混合模型和 N:1 用户空间(即绿色线程)模型,如果出现以下情况,则需要使用非阻塞 I/O它想避免阻塞其他线程。 (例如,如果系统调用返回 EAGAIN 或在将异步读取或写入排队之后进行用户空间上下文切换。)

    【讨论】:

      【解决方案3】:

      内核级线程和用户级线程是有区别的。简单来说:

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

      由于用户级线程需要映射到内核级线程,因此您需要选择合适的映射。您可以将每个用户级别映射到单独的内核级别线程。您还可以将许多用户级映射到一个内核级线程。在后一个映射中,您让多个并发执行路径由单个线程“正如我们所知”执行。如果这些路径之一阻塞,请记住用户级线程需要让出执行,然后执行(内核级)线程阻塞,这会导致所有其他分配的路径也被有效阻塞。我想,这就是声明所指的。 仅供参考:在 Java 中,用户级线程(您在程序中执行的多线程)由 JVM(即运行时系统)映射到内核级线程。


      相关内容:

      【讨论】:

      • 关于最后一句话,如果Project Loom 成功,Java 将能够将许多 JVM 管理的 虚拟线程 映射到每个主机操作系统管理的“真实”线程.这带来了巨大的效率,甚至可以实现数百万个并发线程。
      • 至少在 Java 的早期,用户级线程被称为“绿色线程”,用于在不支持本机线程的操作系统上实现 Java 线程。还有一篇 Wiki 文章:en.wikipedia.org/wiki/Green_threads。 Solaris 上的 IIRC 还使用了 N:M 模型,其中 N 个用户空间线程可能由少于 N 个内核线程处理,引入了存在于OP的报价。
      • 现在有点意思了。还有一件事,大多数现代操作系统不都使用内核和用户线程的一对一映射吗?我可以理解,在旧操作系统可能使用多对一实现的情况下,这个论点将成立。所以也许所有这些网站都只是从一些过时的文本源中复制......
      • @AMANKUMAR 是的,我想是的。我搜索了您可能想到的来源,并注意到其中大多数只是在没有任何上下文的情况下删除了该声明。如果您不知道什么是“用户级线程”以及在这种情况下如何理解“对等线程”,那么这是非常具有误导性的。
      • @AMANKUMAR 操作系统不(真的)进行映射;这是执行程序的程序或运行时系统的责任。想象一下,你会在你的程序中实现一个轻量级的操作系统,它可以同时做一些事情,并想象你的程序是由一个实际的线程执行的。您实现的操作系统的线程是用户级线程。这个例子很奇怪,但我希望它有点帮助。
      猜你喜欢
      • 2018-08-13
      • 1970-01-01
      • 2017-10-06
      • 2013-11-16
      • 1970-01-01
      • 1970-01-01
      • 2015-12-04
      • 2016-01-27
      • 1970-01-01
      相关资源
      最近更新 更多