12th Chapter Concurrent Programming
A process is program-level concurrency, and a thread is a function-level concurrency.
Three basic ways to construct concurrent programs:
- process: Each logical control flow is a process that is scheduled and maintained by the kernel.
- I/O Multiplexing : applications explicitly dispatch their own logical streams in the context of a process.
- Thread: A logical stream running in a single process context that is dispatched by the kernel.
12.1 Process-based concurrency programming
the simplest way to construct a concurrency program is to use the process.
Use functions that are familiar to everyone, such as:
About sharing state information between parent and child processes: share a file table, but do not share the user address space.
A process-independent address space is both an advantage and a disadvantage:
- Pros: Prevent virtual memory from being overwritten incorrectly
- Cons: High overhead, requires IPC mechanism for sharing state information
12.2 Concurrent programming based on I/O multiplexing
is to use the Select function to require the kernel to suspend a process and return control to the application only after one or more I/O events have occurred.
The Select function handles a collection of type Fd_set, also known as a descriptor collection.
The SELECT function has two inputs: a set of descriptors called a read collection and the cardinality (n) of the envy and the Read collection (which is actually the maximum cardinality of any descriptor collection). The Select function blocks until at least one descriptor in the Read collection is ready to be read. When and only if a request to read a byte from the descriptor is not blocked, the descriptor K indicates that it is ready to be read.
As a side effect, select modifies the fd_set that the parameter Fdset points to, indicating that a subset of the Read collection is called a prepared set. The value returned by the function indicates the cardinality of the prepared collection. Because of this side effect, we must update the collection every time we call the Select function.
I/O multiplexing can be used as the basis for concurrent event drivers .
The server uses I/O multiplexing and detects the occurrence of input events with the Select function.
12.3 Thread-based concurrency programming
A thread is a logical flow that runs in the context of a process.
A thread-based logical flow combines the characteristics of a process-based and I/O multiplexing-based stream.
Each process begins its life cycle as a single thread, which is called the main thread. At some point, the main thread creates a peer thread that, starting at this point in time, runs concurrently on two threads.
In some important respects, thread execution is different from the process, because the context of a thread is much smaller than the context of a process, and the context switch of a thread is much faster than the context of the process.
The other difference is that threads are not organized as a process, not by a strict parent-child hierarchy. A thread that is associated with a process makes up a peer (thread) pool, independent of the threads created by other threads.
The main thread is always the first one running in the process. The primary effect of the concept of a peer (thread) pool is that a thread can kill any of its peer threads, or wait for any of its peers to terminate. In addition, each peer thread can read and write the same shared data.
A POSIX thread is a standard interface for processing threads in a C program. The basic usage is:
- The code of the thread and the local data are encapsulated in a thread routine
- Each thread routine takes a generic pointer as input and returns a generic pointer.
The concept of a universal function needs to be mentioned here.
Universal function:
void func (void parameter)
typedef void (uf) (void para)
That is, the input is the pointer, pointing to the actual data to be transferred to the function, if there is only one to direct the pointer to the data, if a lot of them into a struct, so that the pointer to the structure of the body. The latter method is the idea of using the universal function.
This is also the case with threading routines.
To create a thread:
To terminate a thread:
Reclaim resources for terminated threads:
Detach Process:
Initialize Thread:
12.4 Shared variables in multi-threaded programs
A variable is shared when and only if multiple threads refer to an instance of the variable.
A set of concurrent threads runs in the context of a process.
Each thread has its own thread context :
- A unique integer thread Id--tid
- Stack
- Stack pointer
- Program counter
- General purpose Registers
- Condition code
In threaded C programs, variables are mapped to virtual storage according to their storage type:
- Global variables
- Local automatic variables
- Local static variables
The variable v is shared-when and only if one of its instances is referenced by more than one thread.
12.5 Synchronizing threads with semaphores
Sharing variables is convenient, but it also introduces the possibility of synchronization errors.
Here's a key point: Generally, you have no way to predict whether the operating system will choose the correct order for your thread.
Progress Chart:
A progress map is the execution of n concurrent threads modeled as a trace line in an n-dimensional Cartesian space, where the origin corresponds to the initial state of no thread completing an instruction.
When n=2, the state is relatively simple, is more familiar with the two-dimensional coordinate diagram, the horizontal ordinate each represents a thread, and the conversion is represented as a forward edge
Conversion rules:
- The legitimate conversion is to the right or up, that is, one instruction in a thread is completed
- Two directives cannot be completed at the same time, i.e. diagonal lines are not allowed
- The program cannot run in reverse, i.e. it cannot appear down or to the left
The execution history of a program is modeled as a trace line in the state space .
12th Chapter Concurrent Programming Learning Notes