I have been reading Java Concurrency in Practice by Brian Goetz and inside the section Stack Confinement it is mentioned that each thread gets its own stack and so local variables are intrinsically confined to the executing thread; they exist on the executing threads stack, which is not accessible to other threads. What does he mean that each thread has its own execution stack ?
Java Multithreading – Conceptually What Does It Mean When Each Thread Gets Its Own Stack?
concurrencyjavamultithreadingstack
Related Solutions
You do not need to use dedicated threads: a thread is an expensive object so it is generally better to use the thread pool since it will reuse threads to process your work items. Using a dedicated thread is a good idea if you need thread-local storage, thread identity, thread priorities, custom scheduling (picking work item from a stack or a priority queue rather than a queue), a higher priority than work items (if you have thousands of work items in the queue, a thread is likely to get a time slice earlier) or conversely if you want to leave the queue free for something else, etc.
Enqueuing a work item on the thread pool is still a moderately expensive operation (above hundreds of nanoseconds). Besides the algorithm used to dynamically adjust the number of threads in the pool works best when each work item is not too long (let's say less than one millisecond). If your work items are longer than this or if they suffer a poor scalability (database or IO contention), you may want to limit the max number of threads in the pool.
Some timers will enqueue their callbacks over the ThreadPool and some others on the UI. You need to check whether your timer will conflate with any other work you could have enqueued, especially long-running work items on the ThreadPool, and if it is important that your callbacks run before pending work items.
Async/await are mostly necessary when the identity of the thread matters, typically for agents. For example the UI typically can only be manipulated from the UI thread (this is a thread affinity mechanism that serves as a synchronization pattern) and you often need to dispatch a task to a background thread from the UI thread then continue it later over the UI again. While on one hand your services look like agents, on the other hand it may be a design mistake as I just explained and anyway it does not seem like you need to dispatch continuations back to the service.
However tasks are a nice abstraction to use and Parallel.Foreach, Task.Run and others will use the thread pool by default.
Edited the async/await section to take into account the comments below from Robert Harvey Edited to mention timers's callbacks.
Question #1: Not really
Goroutines are a bit weird. They are somewhat similar to fibers, but also somewhat similar to threads.
- They might be preempted.
- They might be concurrent.
- They might share resources.
- They often block on a queue (channel).
- They have their own stacks.
- They are not directly scheduled by the OS, but by the golang runtime.
The golang runtime usually starts up to GOMAXPROCS
threads by default and schedules your goroutines among them.
On any given thread, a goroutine will run until completion or until it blocks on a channel.
That means you can think of goroutines as fibers shared among threads.
This means that you can think of goroutines that don't access global state like you would think of fibers. But for goroutines that do access global state, you need to treat of them like threads.
Question #2: Yes
You need to be mindful when accessing global state!
However, the default communication mechanism, channels, synchronizes access to shared resources, which eases concurrent programming in Go by a lot.
Question #3: Not in the standard library
If you really want to, you could start threads by writing a library in C for Go that gives you access to the underlying OS thread functions (like pthread_create
).
However, I strongly doubt you could use goroutines and channels on threads created in this way, as the golang runtime scheduler has no knowledge of them.
This might also cause problems with calls to other libraries (like the standard library!) that assume access to goroutines and channels.
In conclusion, I don't think directly creating and managing threads in Go is a good idea.
Best Answer
You know when you break to the debugger for whatever reason, and the IDE gives you a stack trace? And each method (stack frame) has its own set of local variables that you can examine in the debugger?
That's the "execution stack" of your program. It shows what the local state of your program looks like at the moment. What the author is saying is that each thread gets its own distinct execution stack like that. It has its own call stack, and each of the methods have their own local variables.
Because the variables are stored as part of the execution stack and not in the heap, they are unique to the thread that's being run and can't be shared directly. You can copy them, or pass references to objects to other threads in various ways, though, so that's mostly an academic distinction.