onevcat / OneV-s-Den-Comments

0 stars 0 forks source link

2021/09/structured-concurrency/ #22

Open utterances-bot opened 2 years ago

utterances-bot commented 2 years ago

Swift 结构化并发 | OneV's Den

本文是我的新书《Swift 异步和并发》中的部分内容,介绍了关于 Swift 中结构化并发的相关概念。如果你对学习 Swift 并发的其他话题有兴趣,也许这本书可以作为参考读物。

https://onevcat.com/2021/09/structured-concurrency/

Donny8028 commented 2 years ago

你好, 我有购买您的书籍, 不知道為什麼, 但里面有一个地方着实有点疑惑, 想请教为什么这个rethrows 不会报错呢?

error: 'rethrows' function must take a throwing function argument

extension AsyncSequence {
    func rethrowsMethod() rethrows {
    }
}

谢谢你

onevcat commented 2 years ago

@Donny8028

感觉应该是编译器的 bug,按照语言 ref 的话,这个情况应该是需要报错的...

A function or method can be declared with the rethrows keyword to indicate that it throws an error only if one of its function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods. Rethrowing functions and methods must have at least one throwing function parameter.

onevcat commented 2 years ago

找了一下源码,AsyncSequence 是一个特例,它被标注了 @rethrows。而这个错误除了检查函数参数有没有 throws 以外,对于 @rethrows 标注的类型整个都是例外

onevcat commented 2 years ago

关于 @rethrows 的话,看上去的主要用处是放宽 protocol 的限制,让非 rethrows 的实现也能满足协议要求。

A by-conformance rethrows witness cannot witness a by-conformance rethrows requirement unless the protocol is @rethrows.

cntrump commented 2 years ago
DispatchQueue.global().async {
    Task {

        print("enter task, is main: \(Thread.isMainThread)")

        await withTaskGroup(of: Void.self, body: { group in
            group.addTask {
                print("do task, is main: \(Thread.isMainThread)")
            }
        })

        print("done, is main: \(Thread.isMainThread)")
    }
}

输出结果

enter task, is main: true
do task, is main: false
done, is main: true

请教一下,Task 里面是否运行在主线程是怎么决定的?

onevcat commented 2 years ago

个人建议最好不要把线程的概念和 Task 的运行上下文 (调度队列) 混合起来。虽然有迹可循 (比如你的这段代码可能是在 viewDidLoad 这样的 @MainActor 中被执行的),但 Swift 并发不保证任务的执行线程。用 Thread.isMainThread 去做这样的线程检测,虽然能得到正确的结果,但是并没有太多意义。

关于这个问题,在书的最后一章进行了一些稍微深入的讨论,不妨去看看。

cntrump commented 2 years ago

奇怪了,这本书我在预售的时候就付款了,还提示我以后书完成后会有邮件通知。 现在没有收到过书完成的通知,输入邮件地址登录也没有收到登录邮件。

onevcat commented 2 years ago

@cntrump 那感觉上可能是当初邮箱就填错了...可以联系一下 mail@objccn.io,提供一下尽可能多的信息(比如原来期望使用的邮箱,包括时间和日期的支付凭证之类的),应该可以帮您从订单信息里刨出来。

hanks-hu commented 2 years ago

你好,这个文章写的挺好的,我想买书看下,但是我有几个问题

  1. 现在swift5.5的await 爆出很多问题,后续API变更,书也会更新吗?还需要再买一次吗?
  2. 我看马上到11.11了,有优惠活动吗?
onevcat commented 2 years ago

电子书有一年免费更新

只要有值得写的新内容,是永久免费更新的。

我看马上到11.11了,有优惠活动吗?

会有优惠,敬请期待。可以关注一下我的微博或者 Twitter,会第一时间发布优惠信息。

hanks-hu commented 2 years ago

😄 好的,多谢了

Donny8028 commented 2 years ago

你好, 我有新的问题想请教, 书中提到AsyncSequence在第二次 for loop 时, 会从第一个值开始返回, 不会接续在第一个for loop之后, 试过(url.lines, notification(names:)之后确实如此, 但有个现象如下

let stream = AsyncStream<Int> { continuation in
        var count = 0
        let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
            let currentCount = count + 1
            continuation.yield(currentCount)
            count = currentCount
        })
}
Task {
    for await current in stream {
        print("first loop value:", current)
        if current > 10 { break }
    }

    for await current in stream {
        print("second loop value:", current)
    }
}

这是一个timer不停止的序列, 只要continuation不呼叫 finish 就不会停止, 所以我尝试下两次 loop的结果, 发现第二次的loop会接续在第一次的结果之后, 难道AsyncStream是个例外吗?🤔

onevcat commented 2 years ago

@Donny8028

可能您有点误解,AsyncSequence 的多次迭代,会不会从头开始,是由满足这个协议的类型中的 makeAsyncIterator 的具体实现方式决定的:如果每个 makeAsyncIterator 都返回一个新的 iterator (或者说,这个序列迭代满足值语义),那么每次 for await 就从头开始;否则,如果每次 makeAsyncIterator 返回的是已有的值,那么这时序列就处于引用语义,每次 for await 都从之前的地方开始。

具体到 AsyncStream<Int> { continuation in } 这个方式生成的序列,最简单的确认方法就是在闭包中尝试打印一个值,然后多次 for await,看看有没有被多次创建,就知道结论了:

let stream = AsyncStream<Int> { continuation in
    print("Called") // 如果打印了两次的话,说明每次 `for` 都创建了新的 Iterator
    var count = 0
    // ...
}
Task {
    for await current in stream {
        if current > 10 { break }
        // ...
    }

    for await current in stream {
        // ...
    }
}
Donny8028 commented 2 years ago

确实, 感谢回覆 👍

phantomAssassion commented 2 years ago

喵神,为什么现在通过Task API提交的闭包,它的处理队列cooperative是个Concurrent类型的?我看书里面所给出的是serial类型队列?

BackWorld commented 2 years ago