C++11线程指南(6)--共享内存与互斥

目录

1. 共享内存

2.使用互斥量mutex

3.共享数据问题

4.互斥量mutex释放


1. 共享内存

  先回顾一下前面几章中用到的,一个存在资源竞争的例子:

#include<iostream>
#include<thread>

void ThreadFunc() {
    for (int i = 1; i <= 8; ++i)
        std::cout << "thread function: " << i << "\n";
}
int main()
{
    std::thread thr(&ThreadFunc);
    for (int i = 1; i <= 8; ++i)
        std::cout << "main thread: " << i << "\n";
    thr.join();
    return 0;
}

  运行结果为:
main thread: 1
main thread: 2
main thread: 3
main thread: 4
main thread: 5
main thread: 6
main thread: 7
thread function: main thread: 8
1
thread function: 2
thread function: 3
thread function: 4
thread function: 5
thread function: 6
thread function: 7
thread function: 8

  可以看到,结果是无序的。

2.使用互斥量mutex

下面使用互斥对线程间进行同步。

#include<iostream>
#include<thread>
#include<string>
#include<mutex>

std::mutex mu;

void PrintFunc(std::string msg, int id) {
	mu.lock();
	std::cout<<msg<<":"<<id<<std::endl;
	mu.unlock();
}

void ThreadFunc() {
	for(int i=1;i<=8;++i)
		PrintFunc("thread function", i);
}

int main()
{
	std::thread thr(&ThreadFunc);
	for(int i=1;i<=8;++i) 
		PrintFunc("main thread", i);
	thr.join();
	return 0;
}

  运行结果为:
main thread:1
thread function:1
thread function:2
thread function:3
main thread:2
main thread:3
main thread:4
thread function:4
thread function:5
thread function:6
main thread:5
main thread:6
main thread:7
main thread:8
thread function:7
thread function:8

3.共享数据问题

  多线程之间共享数据的问题,很大程度上源自对数据的修改顺序。
  如果共享数据是只读的,则不会存在问题,因为一个线程读数据不会影响到另一个线程读取相同数据。但是,但一个线程或多个线程可以修改数据时,问题就出现了。
  下面介绍的互斥就是用于解决这种问题的。

4.互斥量mutex释放

  下面例子中多个线程同时访问相同的list

#include<iostream>
#include<thread>
#include<list>
#include<algorithm>

std::list<int> myList;

void AddToList(int max, int interval) {
	for(int i=0;i<max;++i) {
		if(i%interval==0)
			myList.push_back(i);
	}
}
void PrintList() {
	for(auto &iter: myList)
		std::cout<<iter<<",";
}
int main() {
	int max = 15;
	std::thread t1(AddToList, max, 1);
	std::thread t2(AddToList, max, 2);
	std::thread t3(PrintList);
	t1.join();
	t2.join();
	t3.join();
	return 0;
}

  运行结果为:
  0,2,4,6,8,10,12,14,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
  或者
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,0,2,4,6,8,10,12,14,
  在对list的操作过程中,t1会插入0到9的整数,而t2会插入每个2的倍数。同时,t3会打印list元素。上面的运行结果显示,打印完全处于无序状态。

  互斥可以说是C++的数据保护中使用最广泛的技术。它可以保护数据,避免资源竞争,但同时也会带来死锁,或者保护了太多的或太少的数据(互斥范围选择),等问题。
  C++中可以使用std::mutex()创建实例,调用lock()来锁定资源,unlock()释放资源。但是,可能存在unLock()没有被调用进行释放。
  例如,lock()之后并且unlock()之前,抛出了一个异常。这样导致资源一直处于被锁状态。所以,在标准C++库中,提供了std::lock_guard类模板。它在构造时会锁定指定的mutex, 析构时解锁,确保一个锁定的资源最后都能解锁。

#include<iostream>
#include<thread>
#include<list>
#include<algorithm>
#include<mutex>

std::list<int> myList;
std::mutex myMutex;

void AddToList(int max, int interval) {
	std::lock_guard<std::mutex> guard(myMutex);
	for(int i=0;i<max;++i) {
		if(i%interval==0)
			myList.push_back(i);
	}
}
void PrintList() {
	std::lock_guard<std::mutex> guard(myMutex);
	for(auto &iter: myList)
		std::cout<<iter<<",";
}
int main() {
	int max = 15;
	std::thread t1(AddToList, max, 1);
	std::thread t2(AddToList, max, 2);
	std::thread t3(PrintList);
	t1.join();
	t2.join();
	t3.join();
	return 0;
}

  运行的其中一种结果为:
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,0,2,4,6,8,10,12,14,
  从上看到,2的整数倍的数是连续的。加了互斥机制后,保证了多线程的顺序性。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 多线程中的互斥访问是指多个线程同时访问共享资源时,需要通过一定的机制来保证同一时间只有一个线程可以访问该资源,以避免数据的不一致性和错误。常见的互斥访问机制包括锁、信号量、互斥量等。在Java中,可以使用synchronized关键字和Lock接口来实现互斥访问。 ### 回答2: 多线程是现代程序设计的常见技术,能有效地提高程序性能和用户体验。在多线程编程中,为了保证数据安全和避免竞争,需要使用互斥访问技术来同步多个线程的访问。本文将介绍互斥访问的实现方式和使用场景。 互斥访问是指在多线程环境下,为了保证共享数据的正确性和一致性,需要使用锁来限制同时只有一个线程可以访问共享数据。在Java语言中,提供了synchronized关键字和Lock接口来实现互斥访问。其中,synchronized关键字是Java语言内部实现的一种语法糖,用于简化锁的使用,而Lock接口则提供了更加灵活和功能丰富的锁实现。 在Java中,使用synchronized实现互斥访问的方式很简单,只需要在多个线程访问共享数据的方法或代码块前添加synchronized关键字即可。例如: public synchronized void add(int value) { count += value; } 这样,在多线程环境下,只有一个线程可以执行add方法,其它线程需要等待执行权。一旦当前线程执行完毕,锁会被释放,其它线程就可以继续争取执行权。 除了synchronized关键字,Java还提供了ReentrantLock实现互斥访问。ReentrantLock是一种可重入的锁,可以允许同一个线程多次获得锁,也可以设置锁的公平性,避免线程饥饿。使用ReentrantLock实现互斥访问的方式如下: public class Counter { private final ReentrantLock lock = new ReentrantLock(); private int count = 0; public void add(int value) { lock.lock(); try { count += value; } finally { lock.unlock(); } } } 在代码中,使用ReentrantLock的lock方法获取锁,并在finally块中使用unlock方法释放锁。这样,就可以保证多个线程同步访问共享数据。 互斥访问最常见的应用场景是对共享数据的读写操作,例如多线程修改同一个列表或缓存。使用互斥访问可以保证线程安全,避免数据损坏和因竞争而产生的异常。 总之,互斥访问是Java多线程中的关键技术,实现方式有多种,开发者需要根据实际情况选择最适合的方式。在多线程编程中,务必注意线程安全和数据一致性,避免因竞争而产生的数据异常。 ### 回答3: 在多线程编程中,有时候会出现多个线程同时访问同一共享资源的情况,这就可能会导致数据的不一致或者出错。为了解决这个问题,就需要使用互斥访问技术来控制对共享资源的访问。 互斥访问技术主要有两种:一种是使用锁机制,即在对共享资源的访问上加锁,保证同一时间只有一个线程能够访问该资源;另一种是使用信号量机制,即在对共享资源的访问上设置一个资源数目,在访问前必须先获取资源,访问后再释放资源。 在Java中,使用互斥访问可以通过synchronized关键字实现。synchronized关键字用于修饰方法或者代码块,保证同一时间只有一个线程能够访问该方法或者代码块对应的资源。 例如,对于以下共享资源的访问: public class SharedResource { private int count = 0; public void increment() { //多个线程同时访问这个方法会导致count的值不准确 count++; } } 可以使用synchronized关键字来实现互斥访问: public class SharedResource { private int count = 0; public synchronized void increment() { count++; } } 这样,多个线程同时访问increment()方法时,只有一个线程能够获取到锁,执行完该方法后释放锁,其他线程才能获取到锁进行访问。 需要注意的是,互斥访问虽然可以保证数据的一致性和完整性,但也会降低程序的并发性能,因此需要在权衡性能和正确性的基础上,选择合适的互斥访问方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值