# Calling an actor method in a sync context: which method is better?

**URL:** <https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183>\
**Category:** Using Swift\
**Tags:** concurrency, actors, global-actors\
**Created:** [July 18, 2025, 4:32pm UTC](https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183 "2025-07-18T16:32:06Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![crontab](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/crontab/32/7374_2.png) [@crontab](https://forums.swift.org/u/crontab)\
**Post date:** [July 18, 2025, 4:32pm UTC](https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183/1 "2025-07-18T16:32:06Z")

</div>

I was wondering, what is the difference between these two calls?

```swift
// A.
func syncFunc() {
    Task {
        await MyActor.shared.anotherSyncFunc()
    }
}

```

```swift
// B.
func syncFunc() {
    Task { @MyActor in
        await MyActor.shared.anotherSyncFunc()
    }
}

```

1. What are the benefits of one or the other? Performance or otherwise.

2. Why does the compiler require `await` in case B? My understanding is in B there will be two hops for no good reason it seems.

---

<div class="post-metadata">

**Author:** ![David\_Smith](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/david_smith/32/37508_2.png) [@David\_Smith](https://forums.swift.org/u/David_Smith)\
**Post date:** [July 18, 2025, 7:17pm UTC](https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183/2 "2025-07-18T19:17:37Z")

</div>

I believe the compiler should be able to remove the extra hop to the global executor from the first one, so (without having checked) I would expect these to be the same

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [July 18, 2025, 7:46pm UTC](https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183/3 "2025-07-18T19:46:48Z")

</div>

assuming the following:

1. the `MyActor` API looks something like this:

```swift
@globalActor
actor MyActor {
    static let shared = MyActor()

    func anotherSyncFunc() {}
}

```

1. there's no configuration that causes `syncFunc()` to be inferred as actor-isolated

then, to address these in reverse order:

> [@crontab](#):
>
> Why does the compiler require `await` in case B? My understanding is in B there will be two async calls for no good reason it seems: task, and then the actor's sync method.

the `await` is required because the compiler does not currently piece together the fact that `@MyActor` and calls to actor-isolated methods of the instance returned by `MyActor.shared` have the same static isolation, so it thinks there is an isolation crossing (hence possible suspension point). if you mark the `anotherSyncFunc()` call as `@MyActor`, then you will no longer be required to await it from contexts that are statically known to have the same isolation. granted, if you actually have any state the actor operates on, that will also need to be marked with the same global actor to avoid the same issue within the actor methods themselves (which perhaps means you're effectively dealing with global state isolated to the singleton actor instance, which can be modeled in various ways).

> [@crontab](#):
>
> What are the benefits of one or the other? Performance or otherwise.

using the global actor annotation in the closure's signature can improve the amount of static reasoning the compiler is able to do, which may lead to fewer annotations being needed (e.g. as in the previous case), and changes actor inference propagation in some instances. additionally, its use in this scenario means that `Task.init` can synchronously enqueue the work directly on `MyActor.shared`'s executor, without first having to potentially 'hop off' an existing actor's executor to do so. this means it may end up being a bit more efficient at runtime, but perhaps more importantly, it also means you won't lose certain ordering guarantees about when the closure will actually be scheduled relative to other things – after the `Task.init` returns, the global-actor-isolated work will be enqueued.

---

<div class="post-metadata">

**Author:** ![crontab](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/crontab/32/7374_2.png) [@crontab](https://forums.swift.org/u/crontab)\
**Post date:** [July 18, 2025, 8:10pm UTC](https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183/4 "2025-07-18T20:10:23Z")

</div>

Thanks for your detailed answer.

> [@jamieQ](#):
>
> if you mark the `anotherSyncFunc()` call as `@MyActor`, then you will no longer be required to await it from contexts that are statically known to have the same isolation

This is something new for me! Aren't global actor methods isolated to their actor? Why do I need to annotate them additionally? (And yes, I tried it and it works, `await` is no longer needed in my case B)

In my particular case I have methods that perform a lot of disk I/O, so they aren't required to be async but by declaring them in a global actor I was hoping to have them executed on a separate thread. So this isn't the case?

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [July 18, 2025, 8:37pm UTC](https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183/5 "2025-07-18T20:37:34Z")

</div>

> [@crontab](#):
>
> Aren't global actor methods isolated to their actor? Why do I need to annotate them additionally?

if you have a declaration like:

```swift
@globalActor
actor MyActor {
    static let shared = MyActor()

    func anotherSyncFunc() {}
}

```

then the instance method `anotherSyncFunc()` is isolated, but it is isolated to an _instance_ of `MyActor`, not necessarily the `shared` instance. that is why the `await` is required in your original example even in the `@MyActor` closure. if you wish to use a type for which _all_ its methods are isolated to the _shared_ `@MyActor` instance, one way you can do so is via an appropriately annotated `class` like:

```swift
@MyActor
final class MyDiskIOManager {
  func anotherSyncFunc() {} // isolated to `@MyActor`
}

```

> [@crontab](#):
>
> In my particular case I have methods that perform a lot of disk I/O, so they aren't required to be async but by declaring them in a global actor I was hoping to have them executed on a separate thread. So this isn't the case?

declaring them in a global actor will cause them to run on the actor's executor. by default that is the system-provided executor serviced by the concurrent thread pool. since you're dealing with IO, it's possible you might want your own custom `SerialExecutor` implementation (that could then be attached to `MyActor` via its [unownedExecutor](https://developer.apple.com/documentation/swift/actor/unownedexecutor) property) so that blocking IO calls don't clog up the shared thread pool (could be backed by a single thread or dispatch queue, or whatever makes sense in your context).

---

<div class="post-metadata">

**Author:** ![crontab](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/crontab/32/7374_2.png) [@crontab](https://forums.swift.org/u/crontab)\
**Post date:** [July 18, 2025, 8:56pm UTC](https://forums.swift.org/t/calling-an-actor-method-in-a-sync-context-which-method-is-better/81183/6 "2025-07-18T20:56:07Z")

</div>

> [@jamieQ](#):
>
> then the instance method `anotherSyncFunc()` is isolated, but it is isolated to an _instance_ of `MyActor`, not necessarily the `shared` instance. that is why the `await` is required in your original example even in the `@MyActor` closure.

Now it makes a lot of sense!

I was actually suspecting that global actors are really useful only for annotation, i.e. you declare a "stub"

```swift
@globalActor
actor MyActor {
    static let shared = MyActor()
}

```

and use annotations where appropriate. But then I thought I'm probably doing something wrong, because having only global actor stubs in the code just doesn't look right.

But it seems to me that's how they should be used most of the time. I wish there was a shorter way of declaring them in Swift!
