一,进程和线程区别
1,进程是程序的一次执行实例,是系统进行资源分配和调度的独立单位。进程具有独立的内存空间、系统资源以及独立的执行序列。每个进程都有其独立的进程控制块(PCB),用于描述进程的状态和相关信息。
2,线程是进程中的一个执行单元,是操作系统进行运算调度的最小单元。线程共享进程的内存空间和系统资源,有自己的执行序列,但没有独立的内存空间。线程之间可以通过共享内存等方式进行通信和同步。
3,进程中包含了线程,线程属于进程。一个进程可以包含多个线程,而每个线程都是进程的一部分。
二,线程互斥,同步方式
在Linux中,线程的互斥(互斥量)和同步(如条件变量、信号量、读写锁等)是多线程编程中非常重要的概念。这些机制帮助开发者避免数据竞争、死锁和其他并发问题。以下是Linux中常用的线程互斥和同步方式:
1,互斥量(Mutex)
互斥量用于保护对共享资源的访问,以确保一次只有一个线程可以访问该资源。
在C/C++中,通常使用pthread_mutex_t类型表示互斥量,并通过pthread_mutex_init、pthread_mutex_lock、pthread_mutex_unlock等函数来初始化、锁定和解锁互斥量。
2,条件变量(Condition Variable)
条件变量与互斥量一起使用,允许线程等待某个条件成立后再继续执行。
当某个条件不满足时,线程可以使用条件变量将自己阻塞,并在条件满足时被唤醒。
在C/C++中,使用pthread_cond_t类型表示条件变量,并通过pthread_cond_init、pthread_cond_wait、pthread_cond_signal等函数来初始化、等待和发送信号。
3,信号量(Semaphore)
信号量是一种更通用的同步机制,可以用于控制多个线程对共享资源的访问。
与互斥量不同,信号量允许多个线程同时访问资源,但会限制同时访问的线程数。
在Linux中,可以使用POSIX信号量(sem_t)或System V信号量(struct sembuf、semid_ds等)。
4,读写锁(Read-Write Lock)
读写锁允许多个线程同时读取共享资源,但只允许一个线程写入。
这对于读操作远多于写操作的场景非常有用,因为它可以提高并发性能。
在C/C++中,可以使用pthread_rwlock_t类型表示读写锁,并通过pthread_rwlock_init、pthread_rwlock_rdlock、pthread_rwlock_wrlock等函数来初始化、读锁定和写锁定。
5, 自旋锁(Spinlock)
自旋锁是一种特殊的互斥量,当线程尝试获取锁失败时,它会在一个循环中持续尝试获取锁,而不是被阻塞。
自旋锁适用于短时间的等待场景,因为它避免了线程切换的开销。但是,如果等待时间较长,自旋锁会浪费CPU资源。
在Linux内核编程中,自旋锁是一个常见的同步机制。
以上就是在Linux中常用的线程互斥和同步方式。在实际应用中,应根据具体场景选择合适的同步机制。
三,生产者和消费者原理
在Linux中,生产者和消费者问题是一个经典的并发编程问题。它描述了两个或多个进程(或线程)共享一个固定大小的缓冲区,并且按照生产者-消费者模式进行交互。生产者负责生成数据并放入缓冲区,而消费者则从缓冲区中取出数据进行处理。
以下是生产者和消费者问题的基本原理以及使用POSIX线程(pthreads)的C语言实现代码:
基本原理
1,共享缓冲区:有一个固定大小的缓冲区用于存储数据。
2,互斥访问:生产者和消费者需要互斥地访问缓冲区,以避免数据竞争。
3,同步机制:使用条件变量、信号量或其他同步机制来确保生产者在缓冲区满时不会继续生产,消费者在缓冲区空时不会继续消费。
C语言实现
以下是一个简单的生产者和消费者问题的C语言实现,使用了POSIX线程、互斥锁和条件变量:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE];
int in = 0;
int out = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER; void *producer(void *arg) { for (int i = 0; i < 100; ++i) { pthread_mutex_lock(&mutex); // 等待缓冲区不满 while ((in + 1) % BUFFER_SIZE == out) { pthread_cond_wait(¬_full, &mutex); } // 生产数据 buffer[in] = i; printf("Produced: %d\n", buffer[in]); in = (in + 1) % BUFFER_SIZE; pthread_cond_signal(¬_empty); // 通知消费者 pthread_mutex_unlock(&mutex); } pthread_exit(NULL);
} void *consumer(void *arg) { for (int i = 0; i < 100; ++i) { pthread_mutex_lock(&mutex); // 等待缓冲区不空 while (in == out) { pthread_cond_wait(¬_empty, &mutex); } // 消费数据 int data = buffer[out]; printf("Consumed: %d\n", data); out = (out + 1) % BUFFER_SIZE; pthread_cond_signal(¬_full); // 通知生产者 pthread_mutex_unlock(&mutex); } pthread_exit(NULL);
} int main() { pthread_t prod, cons; pthread_create(&prod, NULL, producer, NULL); pthread_create(&cons, NULL, consumer, NULL); pthread_join(prod, NULL); pthread_join(cons, NULL); return 0;
}
在这个例子中,生产者和消费者都是无限循环的,但为了简化,我们限制了它们各自只生产或消费100个数据项。注意,在实际应用中,你可能需要设置更复杂的退出条件。
这个代码使用了互斥锁(pthread_mutex_t)来保护对缓冲区的访问,并使用条件变量(pthread_cond_t)来实现生产者和消费者之间的同步。当缓冲区满时,生产者会等待not_full条件变量;当缓冲区空时,消费者会等待not_empty条件变量。每次生产或消费一个数据项后,相应的条件变量会被触发,以通知等待的线程可以继续执行。