V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  lxdlam  ›  全部回复第 4 页 / 共 6 页
回复总数  111
1  2  3  4  5  6  
- `shared_ptr<T>` 的结构由两部分组成:control block 和 data block ,其中 control block 负责控制引用计数,并发安全,但是 data block 无任何并发安全保证。
- `shared_ptr<T>::unique()` 负责检查是否仅有一个用户在使用(即自己)。

所以这是一个比较简单的 CoW(Copy-oon-Write) pattern ,在需要修改时复制一份数据单独维护。
2022-06-21 15:34:46 +08:00
回复了 Chaconne 创建的主题 分享发现 无语得 windows10 自带安全软件把 FRP 当作病毒。。。
这个其实是一个很常见的策略,很多时候 pivoting 用到的知名工具都会被标记有风险,比如 frp ,chisel 等等
2022-04-11 13:45:11 +08:00
回复了 bmpidev2019 创建的主题 分享创造 编程语言是如何实现并发的之操作系统篇
@bmpidev2019 废弃的原因其实是因为 Linux 的线程实现方式的历史原因。https://web.archive.org/web/20040211225937/http://java.sun.com/developer/technicalArticles/Programming/linux/

注意到:
> ... Each Linux thread is created as a process clone operation, which leaves the scheduling of threads to be a task of the process scheduler.

而彼时的 Linux 内核版本离 2003 年真正实现了内核态的抢占调度( 2.6 版本,CFS 算法)还有 3 年,也就是说,这个时候的 Linux 只支持一个时间一个线程在跑,OS 无法主动抢占当前正在执行的线程,必须由用户侧程序来调用 schedule 。换句话说,这个时候创建的线程受到了 Linux Process Scheduler 的制约( Linux Scheduler 调度的最小单元是 Thread ),无法实现真正的并行(操作系统无法调度到其他 CPU 上),同时,这样实现的 Thread 切换跟 OS Thread 切换是一致的,也受到 OS Thread 切换的制约( Context Switch ,内核态-用户态进出)。

> By comparison, on Solaris the Java threads are mapped onto user threads, which in turn are run on Lightweight processes (LWP). On Windows the threads are created inside the process itself. For this reason, creating a large number of Java threads on Solaris and Windows today is faster than on Linux. This means you might need to adjust programs that rely on platform-specific timing to take a little longer on startup when they run on Linux.

正如你文章引用的 Solaris 调度系统图所说,Solaris 的调度存在 LWP 这个中间单元,这个中间单元其实类比于现在并发的 M:N 实现,实际是比较超前的。LWP 能被自由的调度到多个 Kernel Thread 上,多个 Kernel Thread 也可以被自由地调度到每个 CPU Core 上;而 Windows 的调度也是在 Process 的树形结构下调度的。在这两个系统下,由 JVM 实现的调度器能够自由地把 Task 进行低开销地切换(要么发生在 LWP 间,要么发生在 Process 内),系统调度的时候也不仅仅以 Thread 为单位( LWP-Kernel Thread 或者 Process-Thread ),使得在这两个平台上,Green Thread 的调度都是合理的。

当然,随着后面 Linux 的崛起,Java 1.3 虽然放弃了 Green Thread 实现,但是 Project Loom 又把它带回来了,这也是 OpenJDK 认为的,Java 并发调用的未来。
2022-04-11 11:56:16 +08:00
回复了 bmpidev2019 创建的主题 分享创造 编程语言是如何实现并发的之操作系统篇
@lxdlam 好像记忆出现了偏差,Project Loom 应该是 cooperative 的。
2022-04-11 11:49:57 +08:00
回复了 bmpidev2019 创建的主题 分享创造 编程语言是如何实现并发的之操作系统篇
仍然是有问题的哈。

1. Go 的 Goroutine 在 1.14 之后就是基于信号抢占式调度的了( https://go.dev/doc/go1.14 ),因为没有手动的 yield ,并不是 cooperative 的。
2. Java 的 Green Thread 已经在 1.3 之后被 Native Thread 取代,换句话说,现行的 JDK 的原生调度模型等同于 OS Thread 。Java 的新用户侧线程 Project Loom 会是新的 Green Thread 方案,但是仍然是抢占式调度的。
3. 由于硬件和软件的进步,Windows 的 Fibers 已经日渐式微了( https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=102989 ),在日常讨论中常用的 User-Thread 切换已经基本达到一样的性能。
4. 抢占式调度和协作式调度的核心区别不是有新任务时是谁在执行,而是谁发出切换信号:抢占式调度往往在一些重要位置( sleep call ,timer tick )放置了中断信号,通过这个信号通知 scheduler 进行切换;协作式调度则是通过 thread 自己根据执行情况,主动交出控制权。
2022-04-09 12:35:47 +08:00
回复了 bmpidev2019 创建的主题 分享创造 介绍 Go/ Java /C/C++/ Swift 等编程语言是如何实现范型的
@FrankHB 说得很好,感谢!昨天回复的比较着急,今天来看很多地方说的确实是不够准确的,关于 C 的部分我的本意其实是说纯文本替换并没有 type level 的工作,所以不算泛型,最后没写清楚。
2022-02-10 20:22:22 +08:00
回复了 Livid 创建的主题 Nintendo Switch Nintendo Direct 任天堂直面会 2022.2.9 公布的所有新视频
Mario Strikers 是系列第三作了,第一作 2005 NGC ,第二作 2007 Wii ,这次是暌违 15 年的新作。
2021-12-17 15:11:06 +08:00
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@lxdlam 基于终端 -> 基于中断
2021-12-17 15:08:25 +08:00
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@ipwx 其实“非常准确明白这些并发方法”本身是一种提升了异步编程门槛的行为,这一串概念能引出来七八个名词和一大堆文章。我个人是非常乐意看见 goroutine 出现的,虽然确实是 dirty and hack 的,但是它足够简单,确确实实解决了实际问题(虽然我还是认为~~go 是垃圾~~)。

对于语言来说,提供一个尽可能统一的抽象,并让不同的 runtime 去实现不同的做法,再实现不同的生态,是一个非常好的做法,也是 Rust 社区正在采纳的做法。这确实会导致出现“事实标准”这一情况(比如 tokio 现在的绝对领导地位),但是给了用户在 consistent 的 interface 下,可以自主根据 workload 切换运行方式的自由。

实际上,阻塞和不阻塞不是异步问题的本源,归根到底还是我们希望每个任务都能够拿到足够多的时间片去跑,而不是要么被无用的 task 频繁占用 CPU ,要么在等一些其实没有意义事情而导致时间流失。从这两个角度出发,一方面我们尝试通过各种方式来更巧妙地让 task 在正确的时候和地方执行,而另一方面我们也在尽力去把各种有这种所谓“无用等待”的地方做成 pubsub 的事件行为把等待让出来,给其他 task 时间。说来也比较奇妙,CPU 和操作系统设计的时候已经采纳了基于终端的事件通知机制,但是应用层全面用到这个特性还是过去了很久。

至于 Worse is better 还是 Do the right thing ,我们都可以另起一次讨论了 :)。
2021-12-17 14:10:55 +08:00
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@ipwx stackful 和 stackless 最大的区别不是保存调用栈的问题,是可不可以在任意函数中启动异步调用的问题,这个问题最经典的例子就是 js 所谓的有色函数,或者说 async/await 的传染问题。考虑一个非常经典的例子:

```
async function g() { return 123; }

function f(n) {
let x = (await g()) + n;
return x;
}
```

假定这个函数是可以运行的,那就有一个问题:`g` 要被调度走,肯定有其他函数要切入运行,栈要被更改,那当 `g` 执行完的时候,`x` 和 `n` 咋办?我们有两种思路:
1. 调用 `g` 的时候,我们记一下调用栈,这下 `f`, `x` 和 `n` 我都记下来了;
2. 让 `f` 也变成 `g` 一样的东西,这样 `f` 自己就会记录 `x` 和 `n` 的关系。

正好就是有栈和无栈协程的区别。

Project Loom 也打算把 stack 存下来放在 Java Heap 里面,搞成 stackful 的。至于 Fibers 为啥挂了,是因为 Fibers 把一个指挥内核调度的可能性给了用户,误用的可能性更高,比如可能跟系统的带锁操作形成死锁( fiber 在等一个 syscall 之后 yield ,但是系统在等 fiber 释放执行这个 syscall 的资源),也是一种优先级反转的特例。种种情况下,Fibers 虽然 API 没有被干掉,但是用户不咋用了。
2021-12-17 13:29:39 +08:00
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@statumer 这是显然的强加因果。

网络编程的崛起之于协程已经是很早的事情了,实际上,nginx 和 Redis 都是单线程 IO 模型( Nginx 1.17 和 Redis 6 才支持的多线程网络 IO 模型),性能并不比后面很多所谓用了多线程 or 协程的应用差,那么协程在这里的作用是什么?从另一个角度,OpenResty 往 Nginx 里面嵌入了一个 LuaJIT ,底层 IO 逻辑完全没变的情况下,为什么比 Nginx 性能好那么多?这些和协程崛起相关没有任何直接联系。

关于二三点,既然你已经提到了 go ,为什么不去看看 go 的源码实现和 Scheduler 设计? Design Doc ( https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/edit#heading=h.c3s328639mw9 )和 1.17 的源码 ( https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/runtime/proc.go;bpv=1;bpt=0 )都说明了 Go 的工作方式正是我所说的在多核系统下,会用另一个线程去跑 blocking syscall ,对于你的程序来说,这部分 syscall 就是 non-blocking 的。同样在 Java 最新的协程方案 Project Loom 里面,工作方式也是一样的 ( https://blogs.oracle.com/javamagazine/post/going-inside-javas-project-loom-and-virtual-threads )。特别要提示一点,虽然现在 Go 确实在可以 poll 读文件的情况下使用 epoll ,但是在 bsd 环境下 Go 现在还是使用 blocking syscall ,而更早的 Go 版本(我没记错是 1.12 以及之前)同样使用的 blocking 的文件 syscall 去读文件。

实际上,如果你真的对所谓的“只是对有无调用栈的取舍”做出了理解的话,你就不会陷入这种强加因果的关系。对于传统的线程,我们会有两种程度的开销:一个是线程自身占用 memory 的开销,这个和系统的默认栈大小有关,这导致了我们的内存占用会随着线程申请数量,这个线程自身的申请也需要通过 syscall ,时间和内存资源占用都存在 overhead ;另一个角度,虽然根据机器和系统不同数据有差异,但是普遍来说,native thread 切换的开销并不小,对于 常用 Linux 发行版来说,这个切换通常不少于 1ms 。当我们实现了用户侧调度,我们可以通过巧妙地 GC 管理等减少这种异步结构的申请和释放开销,通过剔除和当前应用无关的系统 OS 数据字段同步达到更快的上下文切换,甚至直接基于状态机模型舍弃这部分开销,得到普遍更好的执行时间。更进一步来说,OS Level 的调度是 generic 的,内核针对的是任何任务的调度,而对于我们的应用,我们和 runtime 更容易知道我们的调度侧重点和优势,能够把时间和优先级排布做的更精妙,自然能够带来一定的性能提升,这才是用户侧调度的意义所在,也是协程这类结构的性能提升所在。

我同意异步的 IO 会比起同步 IO 有性能提升,虽然平台不同变量较大,但在普遍比较下 IOCP 确实性能和系统利用率是强于 epoll 的,io_uring 确实是未来的方向。但是这部分的性能提升和线程以及协程本身开销优化没有任何关系,假如我们认为线程和协程切换都是零开销的话,那使用哪种结构都影响不了在同一种 IO 下的调度开销。顺带补充一点,线程的上下文切换和调度也是要 involve syscall 的,用户侧实现调度的话能够减少这些 syscall 的次数。

最后,异步是一个很复杂的概念,但是我们可以抛弃一些严谨性的情况下认为异步就是并发。对于并发来说,根据你观察的 level 不同,多进程对于操作系统来说是并发的,多线程对于应用是并发的,而我多次提到,所谓的协程就是一种用户调度抽象,实际上和多线程相比就是执行模型的差距,本质上和多线程没有区别。

最后,如果你认为在当时文章里描述的 LWP 和 Linux Thread 的调度没有任何区别的话,那可能不求甚解的是你。Java 1.3 是 2002 年发布的,而 Linux 真正实现了内核可抢占是在 2003 年的 2.6 版本。由于 1:N 的设计,当某个 task 开始调用 blocking syscall 的时候,其他的所有 task 都没有机会被唤醒,更别提调度到其他 core 去执行;而对于 Solaris 的调度,LWP 可以任意被调度到可用的 kernal thread 上,进而有机会被调度到其他 CPU core 上( https://www.usenix.org/legacy/publications/library/proceedings/sa92/eykholt.pdf )。这些历史背景才是当时选择的原因。当然,Linux 现在对实时性的支持已经非常好了,所以 Project Loom 重拾历史积淀,再次把用户侧线程引入 Java 。
2021-12-16 16:54:47 +08:00
回复了 publicly 创建的主题 2021 你呢?今年最大的改变是什么?
1. 买了很多以前买不起但是很喜欢的东西;
2. 玩了更多优秀的游戏,看了不少 GDC 演讲,了解了很多以前不知道的技术细节;
3. 又开始写文章了。
2021-12-16 16:24:21 +08:00
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@statumer 协程能不能火起来跟 epoll 没有任何直接联系。

epoll 的出现是因为需要解决 IO 过程中信号等待的问题,能把时间交出来去干一些其他事情,这也就是我说的这个“在内核态实现的调度反而还有点类似协程的味道”。而 syscall 或者 IO 阻塞和协程本身没有任何联系,这都是因为协程是一个用户侧调度的 continuation ,系统层面只能感知到 thread ,如果多个协程按照 1:N 模型跑在同一个线程里面,任意一个有阻塞的 syscall 一定会 block 住,和你是否使用协程无关,反过来说,在使用 epoll 的情况下,把 fd 注册完后扔到一个线程里面去等待,跟你是否使用协程也没有本质的性能区别(如果无视线程切换开销的话)。换句话说,如果我们是一个多核系统,完全可以引入第二个线程在不同的核来做轮询同步 IO ,并在成功后向第一个线程发送消息,对于第一个线程来说,这个过程也是 non-blocking 的,和使用线程或者协程同样没有关系。epoll 就相当于这个“第二个线程”,只是由 Kernel 在机制上做了优化,保证了在单核上也能执行这种高效操作。

如果你要谈为什么 Java 1.1 引入 Green Thread 但是 Java 1.3 之后废弃了 Green Thread ,Oracle 的官方文档解释了这个问题 https://web.archive.org/web/20040211225937/http://java.sun.com/developer/technicalArticles/Programming/linux/,最重要的问题在于 1.1 引入 Green Thread 是配合了 Solaris 的 LWP 的实现,而这个 1:N model 在 Linux 下是做不到真实的并行的,而不是所谓的 syscall 阻塞问题。

协程快不快的问题核心是 focus 在用户调度和内核调度带来的各种切换问题,在不同的执行模型、结构模型下天差地别,而不是简单的异步操作的问题。
2021-12-14 12:06:04 +08:00
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@ipwx Erlang 的 Process 也是很经典的 Actor 实现,在爱立信已经跑了 20 年了
1  2  3  4  5  6  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3747 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 31ms · UTC 05:18 · PVG 13:18 · LAX 22:18 · JFK 01:18
Developed with CodeLauncher
♥ Do have faith in what you're doing.