二.共享数据的保护

Posted xiaobai_cpp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二.共享数据的保护相关的知识,希望对你有一定的参考价值。

文章目录


一、用互斥保护共享数据

1.std::mutex调用lock()加锁,unlock()解锁

2.std::lock_guard利用RAII机制保证发生异常时也能正常unlock

#include <list>
#include <mutex>
#include <algorithm>
#include <iostream>
#include <thread>
std::list<int> some_list;
std::mutex some_mutex;

void add_to_list(int new_value)

    std::lock_guard<std::mutex> guard(some_mutex);
    some_list.push_back(new_value);

bool list_contains(int value_to_find)

    std::lock_guard<std::mutex> guard(some_mutex);
    return std::find(some_list.begin(),some_list.end(),value_to_find)
        != some_list.end();


int main()

    std::thread t(add_to_list,42); 
    list_contains(42);
    t.join();
    std::cin.get();

二、防范死锁

1.std::lock同时锁住多个互斥

2.std::adopt_lock指明互斥已被锁住,不得在构造函数内试图另行加锁

代码如下(示例):

#include <mutex>

class some_big_object
;

void swap(some_big_object& lhs,some_big_object& rhs)


class X

private:
    some_big_object some_detail;
    mutable std::mutex m;
public:
    X(some_big_object const& sd):some_detail(sd)

    friend void swap(X& lhs, X& rhs)
    
        if(&lhs==&rhs)
            return;
        std::lock(lhs.m,rhs.m);
        std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
        std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
        swap(lhs.some_detail,rhs.some_detail);
    
;

int main()

3.C++17实现了std::scoped_lock模板,是std::lock模板的RAII实现

    friend void swap(X& lhs, X& rhs)
    
        if(&lhs==&rhs)
            return;
        std::scoped_lock(lhs.m,rhs.m);
        swap(lhs.some_detail,rhs.some_detail);
    

4.防范死锁的准则

1.避免嵌套锁

假如已经持有锁就应该避免获取第二个锁,万一有确有需求获取多个锁,应采用std::lock模板,借单独的调用动作一次获取全部锁来避免死锁。

2.一旦持锁,就须避免调用用户提供的程序接口

我们已经持有锁,再去调用用户实现的接口,恰好用户接口内部试图获取锁,就可能发生嵌套锁,不过有时这样是在所难免,所以我们又需要另一个新的准则。

3.依从固定顺序获取锁

如果多个锁是必要的,又无法通过std::lock模板在一部操作中完成,我们只能退而求其次,在每个线程内都依从固定的顺序获取这些锁。

5.std::unique_lock灵活加锁

与std::lock_guard比较,std::unique_lock即能很好利用RAII机制,又更加的灵活,可以根据需要,在std::unique_lock对象构造时对mutex对象加锁,也可以在std::unique_lock构造时使mutex处于无锁状态,之后调用std::unique_lock对象的lock()函数择机加锁,也可以接管已经加过锁的mutex,且允许在std::unique_lock对象销毁前调用std::unique_lock的成员函数unlock()解锁。

1.std::unique_lock第二个参数使用默认值

std::mutex m;
void f()

 std::unique_lock<std::mutex> lk(m);//构造时加锁
 //.....处理共享数据的事务
 lk.unlock();//解锁
 //.....处理非共享数据的事务
 lk.lock();//加锁
 //.....处理共享数据的事务

2.std::unique_lock第二个参数传入std::adopt_lock指明互斥已经lock过,std::unique_lock据此接收锁的归属权,不得在构造函数中试图另行加锁。

std::mutex m;
void f()

 m.lock();//互斥已经加锁
 std::unique_lock<std::mutex> lk(m,std::adopt_lock);//指明互斥已经加过锁
 //.....
 lk.unlock();

3.std::unique_lock第二个参数传入std::defer_lock指明互斥处于无锁状态,等以后有需要的时候再调用std::unique_lock对象的lock()加锁

std::mutex m;
void f()

 std::unique_lock<std::mutex> lk(m,std::defer_lock);//构造时无锁状态
 lk.lock();//延后加锁
 //...
 lk.unlock();
 //....

3.在不同作用域转移互斥所有权,std::unique_lock可移动不可复制

准许函数锁定互斥,然后将互斥所有权转移给函数调用者,好让他在一个锁的作用下执行其它操作。

std::mutex m;
std::unique_lock<std::mutex> f()

	std::unique_lock<std::mutex> lk(m);
	return lk;

void hold()

	std::unique_lock<std::mutex> lk(f());
    std::cout << __FUNCTION__ << std::endl; 

6.按适当粒度加锁

只在必要的操作上加锁,避免必要操作外加锁

7.在初始化过程中保护共享数据,std::call_once()

1.类的数据成员线程安全的延迟初始化

#include <mutex>

struct connection_info
;

struct data_packet
;

struct connection_handle

    void send_data(data_packet const&)
    
    data_packet receive_data()
    
        return data_packet();
    
;

struct remote_connection_manager

    connection_handle open(connection_info const&)
    
        return connection_handle();
    
 connection_manager;


class X

private:
    connection_info connection_details;
    connection_handle connection;
    std::once_flag connection_init_flag;

    void open_connection()
    
        connection=connection_manager.open(connection_details);
    
public:
    X(connection_info const& connection_details_):
        connection_details(connection_details_)
    
    void send_data(data_packet const& data)
    
        std::call_once(connection_init_flag,&X::open_connection,this);
        connection.send_data(data);
    
    data_packet receive_data()
    
        std::call_once(connection_init_flag,&X::open_connection,this);
        return connection.receive_data();
    
;

int main()


2.单例模式

1.new生成对象指针,使用std::call_once保证只执行一次初始化

#include <iostream>
#include <thread>
#include <memory>
#include <mutex>
#include <chrono> 

class X

private:
	X() ;
	static void init() 
	
		static huishou hs;
		x = new X();
		std::cout << "调用std::call_once的线程ID: " << std::this_thread::get_id() << std::endl;
	
public:
	static X* getMe()
	
		std::call_once(X::init_flag,X::init);
		return x;
	
	void print()
	
		std::lock_guard<std::mutex> lk(m);
		std::cout << "单例对象地址:"<< this << " 当前线程ID: " << std::this_thread::get_id() << std::endl;
	

private:
	class huishou
	
	public:
		huishou() ;
		~huishou() 
			delete X::x;
		;

	;
	static X* x;
	static std::once_flag init_flag;
    std::mutex m;	
;

X* X::x = nullptr;
std::once_flag X::init_flag;

void f()

	X* x = X::getMe();
	x->print();

int main()

	std::thread t1(f);
	std::thread t2(f);
	std::thread t3(f);
	std::thread t4(f);
	t1.join();
	t2.join();
	t3.join();
	t4.join();

2.C++11静态变量初始化,只会在某一个线程单独发生

#include <iostream>
#include <thread>
#include <memory>
#include <mutex>
#include <chrono> 

class X

private:
	X() ;
public:
	static X& getMe()
	
		static X x;
		return x;
	
	void print()
	
		std::lock_guard<std::mutex> lk(m);
		std::cout << "单例对象地址:"<< this << " 当前线程ID: " << std::this_thread::get_id() << std::endl;
	
private:
	std::mutex m;	
;

void f()

	X& x = X::getMe();
	x.print();

int main()

	std::thread t1(f);
	std::thread t2(f);
	std::thread t3(f);
	std::thread t4(f);
	t1.join();
	t2.join();
	t3.join();
	t4.join();


操作系统文件保护及文件共享

一、文件保护

  • 保护文件数据的安全。

(一)口令保护

  • 优点:保存口令的空间开销不多,验证口令的时间开销也很小。
  • 缺点:正确的“口令”存放在系统内部,不够安全。

(二)加密保护

  • 使用某个“密码”对文件进行加密,在访问文件时需要提供正确的“密码”才能对文件进行正确的解密。

  • 优点:保密性强,不需要在系统中存储“密码”
  • 缺点:编码/译码,或者说加密/解密要花费一定时间。

(三)访问控制

  • 在每个文件的FCB(或索引结点)中增加一个访问控制列表(Access-Control List, ACL),该表中记录了各个用户可以对该文件执行哪些操作。
  • 精简的访问列表:以“组”为单位,标记各“组”用户可以对文件执行哪些操作。如:分为 系统管理员、文件主、文件主的伙伴、其他用户 几个分组。
  • 当某用户想要访问文件时,系统会检查该用户所属的分组是否有相应的访问权限。【系统需要管理分组的信息】

二、文件共享

  • 操作系统为用户提供文件共享功能,可以让多个用户共享地使用同一个文件
  • 注意:多个用户共享同一个文件,意味着系统中只有“一份”文件数据。并且只要某个用户修改了该文件的数据,其他用户也可以看到文件数据的变化。
  • 如果是多个用户都“复制”了同一个文件,那么系统中会有“好几份”文件数据。其中一个用户修改了自己的那份文件数据,对其他用户的文件数据并没有影响。

(一)基于索引结点的共享方式(硬链接)

  • 索引结点,是一种文件目录瘦身策略。由于检索文件时只需用到文件名,因此可以将除了文件名之外的其他信息放到索引结点中。这样目录项就只需要包含文件名、索引结点指针。
  • 索引结点中设置一个链接计数变量 count,用于表示链接到本索引结点上的用户目录项数。
  • 若 count = 2,说明此时有两个用户目录项链接到该索引结点上,或者说是有两个用户在共享此文件。
  • 若某个用户决定“删除”该文件,则只是要把用户目录中与该文件对应的目录项删除,且索引结点的count值减 1。
  • 若 count>0,说明还有别的用户要使用该文件,暂时不能把文件数据删除,否则会导致指针悬空。
  • 当 count = 0 时系统负责删除文件。

(二)基于符号链的共享方式(软链接)

  • 当 User3 访问“ccc”时,操作系统判断文件“ccc”属于 Link 类型文件,于是会根据其中记录的路径层层查找目录,最终找到 User1 的目录表中的“aaa”表项,于是就找到了文件1的索引结点。

以上是关于二.共享数据的保护的主要内容,如果未能解决你的问题,请参考以下文章

C 语言学习笔记:C 语言开发环境搭建

C 语言学习笔记:C 语言开发环境搭建

并发共享数据

操作系统文件保护及文件共享

操作系统文件保护及文件共享

并发编程多线程程序同步策略