面试题答案
一键面试多线程编程与并发编程处理资源共享方式与机制的不同
- 多线程编程
- 锁机制:传统多线程编程广泛使用锁(如互斥锁Mutex等)来保护共享资源。当一个线程想要访问共享资源时,它必须先获取锁。只有获取到锁的线程才能访问资源,访问完成后释放锁,其他线程才有机会获取锁并访问。例如在C++中,使用
std::mutex
来保护共享变量:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; int sharedVariable = 0; void increment() { for (int i = 0; i < 1000; ++i) { mtx.lock(); sharedVariable++; mtx.unlock(); } } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Final value: " << sharedVariable << std::endl; return 0; }
- 信号量:信号量可以控制同时访问共享资源的线程数量。它维护一个计数器,线程获取信号量时计数器减1,释放信号量时计数器加1。当计数器为0时,其他线程无法获取信号量,从而限制了并发访问的数量。
- 锁机制:传统多线程编程广泛使用锁(如互斥锁Mutex等)来保护共享资源。当一个线程想要访问共享资源时,它必须先获取锁。只有获取到锁的线程才能访问资源,访问完成后释放锁,其他线程才有机会获取锁并访问。例如在C++中,使用
- Go语言并发编程
- 通道(Channel):Go语言提倡通过通信来共享内存,而不是共享内存来通信。通道是一种类型安全的队列,用于在不同的goroutine之间传递数据。例如,假设有两个goroutine,一个生产数据,一个消费数据:
package main import ( "fmt" ) func producer(ch chan int) { for i := 0; i < 5; i++ { ch <- i } close(ch) } func consumer(ch chan int) { for val := range ch { fmt.Println("Consumed:", val) } } func main() { ch := make(chan int) go producer(ch) go consumer(ch) select {} }
- sync包下的工具:Go语言也提供了类似传统多线程编程中的锁机制,如
sync.Mutex
和sync.RWMutex
。sync.Mutex
是互斥锁,用于保护共享资源,确保同一时间只有一个goroutine可以访问。sync.RWMutex
是读写锁,允许多个读操作并发执行,但写操作时会独占资源。例如:
package main import ( "fmt" "sync" ) var ( counter int mu sync.Mutex ) func increment(wg *sync.WaitGroup) { defer wg.Done() mu.Lock() counter++ mu.Unlock() } func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go increment(&wg) } wg.Wait() fmt.Println("Final counter:", counter) }
并发编程场景下安全地共享数据示例
- 使用互斥锁:
package main import ( "fmt" "sync" ) type Counter struct { value int mu sync.Mutex } func (c *Counter) Increment() { c.mu.Lock() c.value++ c.mu.Unlock() } func (c *Counter) GetValue() int { c.mu.Lock() defer c.mu.Unlock() return c.value } func main() { var wg sync.WaitGroup counter := Counter{} for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() counter.Increment() }() } wg.Wait() fmt.Println("Final value:", counter.GetValue()) }
- 使用通道:
package main import ( "fmt" ) type Data struct { value int } func process(dataCh chan Data) { for data := range dataCh { data.value++ fmt.Println("Processed data:", data.value) } } func main() { dataCh := make(chan Data) go process(dataCh) for i := 0; i < 5; i++ { data := Data{value: i} dataCh <- data } close(dataCh) }
在上述代码中,使用互斥锁时,通过Lock
和Unlock
方法保护共享变量counter
的访问。使用通道时,将数据发送到通道中,由接收方进行处理,避免了直接共享内存带来的竞争问题。