二.共享数据的保护
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的索引结点。
以上是关于二.共享数据的保护的主要内容,如果未能解决你的问题,请参考以下文章