迅速演员是并发的,但不是平行的吗?

发布于 2025-02-13 03:06:52 字数 1514 浏览 1 评论 0原文

我试图弄清楚什么演员带来了什么。 对我来说并不清楚 - 它们是真正的平行还是并发。

我几乎没有进行检查以检查自己:

actor SomeActor {
    func check() {
        print("before sleep")
        sleep(5)
        print("after sleep")
    }
}

let act1 = SomeActor()
let act2 = SomeActor()
Task {
    await withTaskGroup(of: Void.self) { group in
        group.addTask {
            await act1.check()
        }

        group.addTask {
            await act2.check()
        }
    }
}

输出是:

before sleep // Waiting 5 sec
after sleep
before sleep // Starting after 5 sec
after sleep

我阻止当前的执行程序线程,以免产生它。 但是第二个任务不是并行启动。

那么,这是否意味着同一Actor类型共享执行程序实例的每个实例?

如果是这样,那么同一演员的多个实例不支持真正的并行性,而是同时执行?

UPD:

我有一些新的观察结果。因此,上面的示例在我们运行group.add()时使用继承的隔离。因此,它不能立即通过第二演员电话。

我的第二个观察结果 - 当我们以这种方式运行任务时:

        let act1 = SomeActor()
        let act2 = SomeActor()
        
        Task.detached {
            await act1.check()
        }
        Task { @MainActor in
            await act2.check()
        }

输出是:

before sleep
before sleep
after sleep
after sleep

它是真正的平行,

但是当我使用独立任务时,输出序列相同:

        let act1 = SomeActor()
        let act2 = SomeActor()
        
        Task.detach {
            await act1.check()
        }
        Task.detached { @MainActor in
            await act2.check()
        }

输出为:

before sleep
after sleep
before sleep
after sleep

I'm trying to figure out, what actor brings to use.
It's not clear for me - are they truly parallel or just concurrent.

I did little test to check myself:

actor SomeActor {
    func check() {
        print("before sleep")
        sleep(5)
        print("after sleep")
    }
}

let act1 = SomeActor()
let act2 = SomeActor()
Task {
    await withTaskGroup(of: Void.self) { group in
        group.addTask {
            await act1.check()
        }

        group.addTask {
            await act2.check()
        }
    }
}

Output is:

before sleep // Waiting 5 sec
after sleep
before sleep // Starting after 5 sec
after sleep

I block current executor thread so it doesn't yield it.
But second task doesn't start in parallel.

So does it mean that each instance of the same actor type share executor instance?

If so then multiple instance of same actor don't support truly parallelism, but concurrent execution?

UPD:

I have a little bit new observations. So example above use inherited isolation when we run group.add(). So it can't pass second actor call immediately.

My second observation - when we run tasks this way:

        let act1 = SomeActor()
        let act2 = SomeActor()
        
        Task.detached {
            await act1.check()
        }
        Task { @MainActor in
            await act2.check()
        }

Output is:

before sleep
before sleep
after sleep
after sleep

So it's truly parallel

But when I use detached task, output is serial the same:

        let act1 = SomeActor()
        let act2 = SomeActor()
        
        Task.detach {
            await act1.check()
        }
        Task.detached { @MainActor in
            await act2.check()
        }

Output is:

before sleep
after sleep
before sleep
after sleep

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

木緿 2025-02-20 03:06:52

如果您添加一些额外的调试,可能会为您提供帮助:

actor SomeActor {
    var name: String
    init(name: String) {
        self.name = name
    }

    func check() {
        print("Actor: \(name) check(), sleeping now, blocking thread \(Thread.current)")
        sleep(1)
        print("Actor: \(name), sleep done, unblocking thread \(Thread.current)")
    }
}

let act1 = SomeActor(name: "A")
let act2 = SomeActor(name: "B")
Task() {
    print("in task, on thread: \(Thread.current)")
    await withTaskGroup(of: Void.self) { group in
        group.addTask {
            print("Group task: 1 (on thread: \(Thread.current)")
            await act1.check()
        }

        group.addTask {
            print("Group task: 2 (on thread: \(Thread.current)")
            await act2.check()
        }
    }
}

任务就像派遣到全局队列一样,其优先级是从任务中获取的。 如果您阻止了任务使用的线程

,则使用 产生线程的线程(不要这样做)。如果您使用异步睡眠,请尝试?等待任务。睡觉然后您会看到合作行为:

actor SomeActor {
    var name: String
    init(name: String) {
        self.name = name
    }

    func check() async throws {
        print("Actor: \(name) check(), sleeping now, yielding thread \(Thread.current)")
        try await Task.sleep(nanoseconds: NSEC_PER_SEC)
        print("Actor: \(name), Task.sleep done (was not cancelled), back now on thread \(Thread.current)")
    }
}

let act1 = SomeActor(name: "A")
let act2 = SomeActor(name: "B")
Task() {
    print("in task, on thread: \(Thread.current)")
    await withTaskGroup(of: Void.self) { group in
        group.addTask {
            print("Group task: 1 (on thread: \(Thread.current)")
            try? await act1.check()
        }

        group.addTask {
            print("Group task: 2 (on thread: \(Thread.current)")
            try? await act2.check()
        }
    }
}

给出类似的输出:

in task, on thread: <NSThread: ...>{number = 4, name = (null)}
Group task: 1 (on thread: <NSThread:...>{number = 4, name = (null)}
Actor: A check(), sleeping now, yielding thread <NSThread: ...>{number = 4, name = (null)}
Group task: 2 (on thread: <NSThread: ...>{number = 4, name = (null)}
Actor: B check(), sleeping now, yielding thread <NSThread: ...>{number = 4, name = (null)}
Actor: A, Task.sleep done (was not cancelled), back now on thread <NSThread: ...>{number = 7, name = (null)}
Actor: B, Task.sleep done (was not cancelled), back now on thread <NSThread: ...>{number = 7, name = (null)}

It might help you if you add in some extra debug like so:

actor SomeActor {
    var name: String
    init(name: String) {
        self.name = name
    }

    func check() {
        print("Actor: \(name) check(), sleeping now, blocking thread \(Thread.current)")
        sleep(1)
        print("Actor: \(name), sleep done, unblocking thread \(Thread.current)")
    }
}

let act1 = SomeActor(name: "A")
let act2 = SomeActor(name: "B")
Task() {
    print("in task, on thread: \(Thread.current)")
    await withTaskGroup(of: Void.self) { group in
        group.addTask {
            print("Group task: 1 (on thread: \(Thread.current)")
            await act1.check()
        }

        group.addTask {
            print("Group task: 2 (on thread: \(Thread.current)")
            await act2.check()
        }
    }
}

The Task is like dispatching onto a global queue, the priority of which is taken from Task.init's priority: argument

If you block the Thread that the task is using, with sleep (don't do that) that's not yielding the thread. If you use the async sleep, the try? await Task.sleep then you see the cooperative behaviour:

actor SomeActor {
    var name: String
    init(name: String) {
        self.name = name
    }

    func check() async throws {
        print("Actor: \(name) check(), sleeping now, yielding thread \(Thread.current)")
        try await Task.sleep(nanoseconds: NSEC_PER_SEC)
        print("Actor: \(name), Task.sleep done (was not cancelled), back now on thread \(Thread.current)")
    }
}

let act1 = SomeActor(name: "A")
let act2 = SomeActor(name: "B")
Task() {
    print("in task, on thread: \(Thread.current)")
    await withTaskGroup(of: Void.self) { group in
        group.addTask {
            print("Group task: 1 (on thread: \(Thread.current)")
            try? await act1.check()
        }

        group.addTask {
            print("Group task: 2 (on thread: \(Thread.current)")
            try? await act2.check()
        }
    }
}

Gives output like:

in task, on thread: <NSThread: ...>{number = 4, name = (null)}
Group task: 1 (on thread: <NSThread:...>{number = 4, name = (null)}
Actor: A check(), sleeping now, yielding thread <NSThread: ...>{number = 4, name = (null)}
Group task: 2 (on thread: <NSThread: ...>{number = 4, name = (null)}
Actor: B check(), sleeping now, yielding thread <NSThread: ...>{number = 4, name = (null)}
Actor: A, Task.sleep done (was not cancelled), back now on thread <NSThread: ...>{number = 7, name = (null)}
Actor: B, Task.sleep done (was not cancelled), back now on thread <NSThread: ...>{number = 7, name = (null)}
不疑不惑不回忆 2025-02-20 03:06:52

在回答您的问题时,独立演员实例可以并行运行(只要不受限制合作线程池)即可。当我运行您的代码片段时,我会喜欢并行执行,因此我怀疑您的测试中还有其他一些方面产生了串行行为。我们需要一个 mcve 才能进一步诊断。

例如,如果您在iOS模拟器上运行它,则具有人为约束的协作线程池。请参阅带有异步 - WAIT任务组的最大线程数。尽管我无法表现出您描述的行为,但模拟器的受约束合作线池很容易导致一个对潜在平行性的推论。

如果您在模拟器上运行此操作,请尝试在物理设备或MACOS目标上进行测试,您将享受一个合作的线程池,该池将利用设备上所有可用的内核。

In answer to your question, independent actor instances can run in parallel (as long as the cooperative thread pool is not constrained). When I ran your code snippets, I enjoyed parallel execution, so I suspect that there is some other aspect of your tests that yielded the serial behavior. We need a MCVE to diagnose this further.

For example, if you run this on an iOS simulator, that has an artificially constrained cooperative thread pool. See Maximum number of threads with async-await task groups. While I have been unable to manifest the behavior you describe, the simulator’s constrained cooperative thread pool can easily lead one to incorrect inferences about the potential parallelism.

If you ran this on a simulator, try testing this on a physical device or a macOS target, and you will enjoy a cooperative thread pool that will avail itself of all of the cores available on your device.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文