In the introductory section of the Concurrency chapter of "The Swift Programming Language" I read:
When an asynchronous function resumes, Swift doesn’t make any
guarantee about which thread that function will run on.
This surprised me. It seems odd, comparing for example with waiting on semaphore in pthreads, that execution can jump threads.
This leads me to the following questions:
Why doesn't Swift guarantee resuming on the same thread?
Are there any rules by which the resuming thread could be
determined?
Are there ways to influence this behaviour, for example make sure it's resumed on the main thread?
EDIT: My study of Swift concurrency & subsequent questions above were triggered by finding that a Task started from code running on the main thread (in SwiftUI) was executing it's block on another thread.
It helps to approach Swift concurrency with some context: Swift concurrency attempts to provide a higher-level approach to working with concurrent code, and represents a departure from what you may already be used to with threading models, and low-level management of threads, concurrency primitives (locking, semaphores), and so on, so that you don't have to spend any time thinking about low-level management.
From the Actors section of TSPL, a little further down on the page from your quote:
You can use tasks to break up your program into isolated, concurrent pieces. Tasks are isolated from each other, which is what makes it safe for them to run at the same time…
In Swift Concurrency, a Task represents an isolated bit of work which can be done concurrently, and the concept of isolation here is really important: when code is isolated from the context around it, it can do the work it needs to without having an effect on the outside world, or be affected by it. This means that in the ideal case, a truly isolated task can run on any thread, at any time, and be swapped across threads as needed, without having any measurable effect on the work being done (or the rest of the program).
As #Alexander mentions in comments above, this is a huge benefit, when done right: when work is isolated in this way, any available thread can pick up that work and execute it, giving your process the opportunity to get a lot more work done, instead of waiting for particular threads to be come available.
However: not all code can be so fully isolated that it runs in this manner; at some point, some code needs to interface with the outside world. In some cases, tasks need to interface with one another to get work done together; in others, like UI work, tasks need to coordinate with non-concurrent code to have that effect. Actors are the tool that Swift Concurrency provides to help with this coordination.
Actors help ensure that tasks run in a specific context, serially relative to other tasks which also need to run in that context. To continue the quote from above:
…which is what makes it safe for them to run at the same time, but sometimes you need to share some information between tasks. Actors let you safely share information between concurrent code.
… actors allow only one task to access their mutable state at a time, which makes it safe for code in multiple tasks to interact with the same instance of an actor.
Besides using Actors as isolated havens of state as the rest of that section shows, you can also create Tasks and explicitly annotate their bodies with the Actor within whose context they should run. For example, to use the TemperatureLogger example from TSPL, you could run a task within the context of TemperatureLogger as such:
Task { #TemperatureLogger in
// This task is now isolated from all other tasks which run against
// TemperatureLogger. It is guaranteed to run _only_ within the
// context of TemperatureLogger.
}
The same goes for running against the MainActor:
Task { #MainActor in
// This code is isolated to the main actor now, and won't run concurrently
// with any other #MainActor code.
}
This approach works well for tasks which may need to access shared state, and need to be isolated from one another, but: if you test this out, you may notice that multiple tasks running against the same (non-main) actor may still run on multiple threads, or may resume on different threads. What gives?
Tasks and Actors are the high-level tools in Swift concurrency, and they're the tools that you interface with most as a developer, but let's get into implementation details:
Tasks are actually not the low-level primitive of work in Swift concurrency; Jobs are. A Job represents the code in a Task between await statements, and you never write a Job yourself; the Swift compiler takes Tasks and creates Jobs out of them
Jobs are not themselves run by Actors, but by Executors, and again, you never instantiate or use an Executor directly yourself. However, each Actor has an Executor associated with it, that actually runs the jobs submitted to that actor
This is where scheduling actually comes into play. At the moment there are two main executors in Swift concurrency:
A cooperative, global executor, which schedules jobs on a cooperative thread pool, and
A main executor, which schedules jobs exclusively on the main thread
All non-MainActor actors currently use the global executor for scheduling and executing jobs, and the MainActor uses the main executor for doing the same.
As a user of Swift concurrency, this means that:
If you need a piece of code to run exclusively on the main thread, you can schedule it on the MainActor, and it will be guaranteed to run only on that thread
If you create a task on any other Actor, it will run on one (or more) of the threads in the global cooperative thread pool
And if you run against a specific Actor, the Actor will manage locks and other concurrency primitives for you, so that tasks don't modify shared state concurrently
With all of this, to get to your questions:
Why doesn't Swift guarantee resuming on the same thread?
As mentioned in the comments above — because:
It shouldn't be necessary (as tasks should be isolated in a way that the specifics of "which thread are we on?" don't matter), and
Being able to use any one of the available cooperative threads means that you can continue making progress on all of your work much faster
However, the "main thread" is special in many ways, and as such, the #MainActor is bound to using only that thread. When you do need to ensure you're exclusively on the main thread, you use the main actor.
Are there any rules by which the resuming thread could be determined?
The only rule for non-#MainActor-annotated tasks are: the first available thread in the cooperative thread pool will pick up the work.
Changing this behavior would require writing and using your own Executor, which isn't quite possible yet (though there are some plans on making this possible).
Are there ways to influence this behaviour, for example make sure it's resumed on the main thread?
For arbitrary threads, no — you would need to provide your own executor to control that low-level detail.
However, for the main thread, you have several tools:
When you create a Task using Task.init(priority:operation:), it defaults to inheriting from the current actor, whatever actor this happens to be. This means that if you're already running on the main actor, the task will continue using the current actor; but if you aren't, it will not. To explicitly annotate that you want the task to run on the main actor, you can annotate its operation explicitly:
Task { #MainActor in
// ...
}
This will ensure that regardless of what actor the Task was created on, the contained code will only run on the main actor.
From within a Task: regardless of the actor you're currently on, you can always submit a job directly onto the main actor with MainActor.run(resultType:body:). The body closure is already annotated as #MainActor, and will guarantee execution on the main thread
Note that creating a detached task will never inherit from the current actor, so guaranteed that a detached task will be implicitly scheduled through the global executor instead.
My study of Swift concurrency & subsequent questions above were triggered by finding that a Task started from code running on the main thread (in SwiftUI) was executing it's block on another thread.
It would help to see specific code here to explain exactly what happened, but two possibilities:
You created a non-explicitly #MainActor-annotated Task, and it happened to begin execution on the current thread. However, because you weren't bound to the main actor, it happened to get suspended and resumed by one of the cooperative threads
You created a Task which contained other Tasks within it, which may have run on other actors, or were explicitly detached tasks — and that work continued on another thread
For even more insight into the specifics here, check out Swift concurrency: Behind the scenes from WWDC2021, which #Rob linked in a comment. There's a lot more to the specifics of what's going on, and it may be interesting to get an even lower-level view.
If you want insights into the threading model underlying Swift concurrency, watch WWDC 2021 video Swift concurrency: Behind the scenes.
In answer to a few of your questions:
Why doesn't Swift guarantee resuming on the same thread?
Because, as an optimization, it can often be more efficient to run it on some thread that is already running on a CPU core. As they say in that video:
When threads execute work under Swift concurrency they switch between continuations instead of performing a full thread context switch. This means that we now only pay the cost of a function call instead. …
You go on to ask:
Are there any rules by which the resuming thread could be determined?
Other than the main actor, no, there are no assurances as to which thread it uses.
(As an aside, we’ve been living with this sort of environment for a long time. Notably, GCD dispatch queues, other than the main queue, make no such guarantee that two blocks dispatched to a particular serial queue will run on the same thread, either.)
Are there ways to influence this behaviour, for example make sure it's resumed on the main thread?
If we need something to run on the main actor, we simply isolate that method to the main actor (with #MainActor designation on either the closure, method, or the enclosing class). Theoretically, one can also use MainActor.run {…}, but that is generally the wrong way to tackle it.
I am trying to understand how I shall port my Java chess engine to dart.
So I have understood that I should use Isolates and/or Futures to run my engine in parallell with the GUI but how can I force the engine to terminate the search.
In java I just set some boolean that where shared between the engine thread and the gui thread.
You should send a message to the isolate, telling it to stop. You can simply do something like:
port.send('STOP');
To be clear, isolates and futures are two different things, and you use them differently.
Use an isolate when you want some code to truly run concurrently, in a separate "isolated memory heap". An isolate is like a mini program, running separately from your main program. You send isolates messages, and you can receive messages from isolates.
Use a future when you want to be notified when a value is available later. "Later" is defined as "a future tick in the event loop". Each isolate has its own event loop. It's important to understand that just asking a Future to run a function doesn't make the function run in parallel. It just puts the function onto the event loop to be run "later".
Answering the implied question 'how can I get a long running task in an isolate to cease running?' rather than more explicitly asked 'how can I cause an isolate to terminate, release it's resources and generally cease to be?'
Break the long running task up into smaller, shorter running units.
Execute each unit with a Future. Chain futures as appropriate.
Provide a flag that each unit should check before executing its logic. If the flag is set, bail.
Listen for a 'stop' message and set the flag if/when received.
Splitting the main processing task up into Futures allows processing of the stop message to get onto the event queue ahead of units of processing of the main task.
There is now iso.Isolate.kill()
WARNING: This method is experimental and not handled on every platform yet.
I am developing a WPF application with C# 4.0 where some user-specific code will
be compiled at runtime and then executed inside an AppDomain. The process might take 10 ms or 5 minutes. The AppDomain will be created by Task.Factory.StartNew(). Works fine.
Now I want to be able to cancel/interrupt the execution. I can press a
Stop button while the codes is executing but how can I cancel the Task? I know:
there is the CancellationToken.IsCancellationRequested property but I cannot
loop through something. This is why I cannot check the value while executing
the (atomic) code. And unloading the AppDomain does not stop the Task.
FYI: I took the Task class because it easy to use. If Thread would be useful: No problem.
Can someone help me? A short code snippet would be nice :).
Thank you.
Aborting a thread or a task is a code smell of a badly designed solution.
If that is your decision, you should consider that every line of code could be the last one to be executed and consider releasing any unmanaged resource, lock, etc that could leave the system in an inconsistent state. In theory we should always be this careful, but in practice this doesn't hold.
If you try with a Thread and the inviting .Abort() method, you should consider that ThreadAbortException is a special exception in terms of try-catch and finally blocks. Additionally, you can't even be sure that the thread is going to be aborted.
In regards of using a Task, AFAIK (I'm not an expert in TPL) I'm afraid you cannot do what you want. You should somehow re-design your logic to consider the cancellation token and cleanly stop your computation.
In Linux, with POSIX threads, is it possible to hint the scheduler to schedule a particular thread. Actually the scenario is that I have a process which is replica of another process. For deterministic execution, the follower process needs to acquire the locks in the same order as the leader process.
So for example, say in leader process, mutex a is locked by first thread 2, then 3 and 4. The follower must execute in the same order. So if in follower, thread 3 first encounters mutex a, I want thread 3 to say to the scheduler, ok I'm giving up my time slice, please schedule thread 2 instead. I know this can be achieved by modifying the scheduler, but I do not want that, I want to be able to control this from user space program.
In any system, Linux, Windows POSIX or not, if you have to ask this sort of question then I'm afraid that your app is heading for a dark place :(
Even if thread 3 were to yield, say with sleep(0), an interrupt straight after might well just schedule thread 3 back on again, preempting thread 2, or the OS might run thread 3 straightaway on another free core and it could get to the mutex first.
You have to make your app work correctly, (maybee not optimally), independently of the OS scheduling/dispatching algorithms. Even if you get your design to work on a test box, you will end up having to test your system on every combination of OS/hardware to ensure that it still works without deadlocking or performing incorrectly.
Fiddling with scheduling algorithms, thread priorities etc. should only be done to improve the performance of your app, not to try and make it work correctly or to stop it locking up!
Rgds,
Martin
I have to do 3 async operations parallely inside a Windows NT Service (using .Net2.0/C#). For that I am using Backgroundworker component.
Is it a good option/approach?
For continuous operation, I am calling RunWorkerAsync() again in RunWorkerCompleted event.
Please suggest me.
Usually BackgroundWorker is used for long-time operations. If you just need to execute 3 tasks in parallel you can use simple threads.
Usually, BackgroundWorker is used when you need RunWorkerCompleted to perform updates in a GUI. At least that's how I've been using it. It sounds like you want this to run constantly, so why not use a regular worker thread?
The biggest issue I see is that BackgroundWorker uses the .NET thread pool, so if you really want this to run continuously, you've just guaranteed to use up one of the thread pool threads, of which there are a limited number available.
I wouldn't be using backgroundworkers for the job you're describing here.
The background worker is meant to be used for some 'long' running operations while keeping a responsive UI. Using services breaks the UI pattern.
For a service I would be using a timer and three threads.
Let the timer check the existence of the three threads and restart them or report errors when needed. The three threads can do their own job in a while loop (don't forget to add a sleep(0) in there)