1.死锁
1.1 概念
死锁是指两个或两个以上的进程在执行过程中由于竞争资源或者由于彼此之间通信而造成的一种阻塞现象,若无外力作用,它们都将无法继续运行,此时称系统出于死锁状态或系统产生了死锁,这些永远在互相等待的进程称作死锁进程。
1.2 产生的原因
1.系统资源不足;
2.进程(线程)推进的顺序不恰当;
3.资源分配不当。
1.3 死锁的四个必要条件
1.互斥条件:所谓互斥就是进程在某一时间内独占资源;
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
3.不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺;
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
1.4 解决死锁的方法
1.不难看出,在死锁的四个必要条件中,第二、三和四项条件比较容易消除。通过引入事务机制,往往可以消除第二、三两项条件,方法是将所有上锁操作均作为事务对待,一旦开始上锁,即确保全部操作均可回退,同时通过锁管理器检测死锁,并剥夺资源(回退事务)。这种做法有时会造成较大开销,而且也需要对上锁模式进行较多改动。消除第四项条件是比较容易且代价较低的办法。具体来说这种方法约定:上锁的顺序必须一致。具体来说,我们人为地给锁指定一种类似“水位”的方向性属性。无论已持有任何锁,该执行绪所有的上锁操作,必须按照一致的先后顺序从低到高(或从高到低)进行,且在一个系统中,只允许使用一种先后次序。请注意,放锁的顺序并不会导致死锁。也就是说,尽管按照 锁A, 锁B, 放A, 放B 这样的顺序来进行锁操作看上去有些怪异,但是只要大家都按先A后B的顺序上锁,便不会导致死锁。
1.5 自己创建一个死锁的项目
1.更改我们之前写的生产者消费者项目;
2.我们将 消费线程处理函数 中的等待互斥量 ::WaitForSingleObject(pThis->m_hMutexProd, INFINITE) 放到消费产品信号量到来之前,就会产生死锁,这时候由于 m_hMutexProd 只有一个,因此会与生产者线程发生资源抢占现象;
// 消费线程处理函数
DWORD WINAPI C生产者消费者模式Dlg::CustomerThreadProc(LPVOID lpParameter)
{
C生产者消费者模式Dlg* pThis = (C生产者消费者模式Dlg*)lpParameter;
int nID = 0;
while (pThis->m_bQuitFlag)
{
// 将互斥量放到这里就会产生死锁
// ======================在队列中取产品========================
::WaitForSingleObject(pThis->m_hMutexProd, INFINITE);
// ======================等消费产品的信号=========================
if(::WaitForSingleObject(pThis->m_hSemaphoreCons, 10) == WAIT_TIMEOUT)
{
continue;
}
// ======================等消费产品的信号=========================
// 原位置
// ======================在队列中取产品========================
// ::WaitForSingleObject(pThis->m_hMutexProd, INFINITE);
... ...
}
return 0;
}
2.库
2.1 静态库
1.静态库是指在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件;在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中的这种库;
2.静态库特点是可执行文件中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝;
2.2 创建一个静态库
1.创建一个win控制台应用程序,在点击下一步后选择 静态库 选项,空文件,将下面两个勾选的选项取消,点击完成;
2.添加一个源文件,一个头文件,源文件中定义两个函数,头文件中声明这两个函数;
头文件(.h文件)
#ifndef _MYSTATICLIB_H_
#define _MYSTATICLIB_H_
int MyAdd(int a, int b);
int MySub(int a, int b);
#endif //_MYSTATICLIB_H_
源文件(.cpp文件)
int MyAdd(int a, int b)
{
return a+b;
}
int MySub(int a, int b)
{
return a-b;
}
3.点击生成->生成解决方案(不调试的原因是没有main函数,无法进行调试),这时会在 Debug 文件夹下生成一个以 .lib 结尾的文件 MyStaticLib.lib ,这个文件就是创建好的静态库,我们可以新建一个解决方案来调用该静态库;
4.在新建的 Test 项目中创建一个 main.cpp 文件;
5.在 main.cpp 中引用 MyStaticLib.lib 静态库的方法有两种:一种是将 MyStaticLib.lib 与 MyStaticLib.h 这两个文件都拷贝到 main.c 文件的同级目录下,之后添加的代码如下:
#include "iostream"
using namespace std;
// 想要使用静态库两种方法:
// 1.将 .h 文件和 .lib 文件拷贝到当前 main.cpp 文件的同级目录下
#include "MyStaticLib.h"
#pragma comment(lib, "MyStaticLib.lib") // 引用静态库
int main(int argc, char **argv)
{
int add_value = MyAdd(5, 5);
int sub_value = MySub(9, 8);
cout << "加和为:" << add_value << endl;
cout << "减的值为:" << sub_value << endl;
system("pause");
return 0;
}
6.另一种方法是通过路径找到 .h 文件和 .lib 静态库,代码如下:
#include "iostream"
using namespace std;
// 想要使用静态库两种方法:
// 2.直接引用另一个工程的地方,这种方法比较常用
#include "../../MyStaticLib/MyStaticLib/MyStaticLib.h"
#pragma comment(lib, "../../MyStaticLib/Debug/MyStaticLib.lib")
int main(int argc, char **argv)
{
int add_value = MyAdd(5, 5);
int sub_value = MySub(9, 8);
cout << "加和为:" << add_value << endl;
cout << "减的值为:" << sub_value << endl;
system("pause");
return 0;
}
2.3 我们发现创建的静态库只能在 .cpp 文件中使用,无法在 .c 文件中使用
1.创建一个 .c 文件,并按照上述第二种方法引用静态库;
#include <stdio.h>
#include "../../MyStaticLib/MyStaticLib/MyStaticLib.h"
#pragma comment(lib, "../../MyStaticLib/Debug/MyStaticLib.lib")
int main(int argc, char **argv)
{
int add_value = MyAdd(5, 5);
int sub_value = MySub(9, 8);
printf("加和为:%d\n", add_value);
printf("减的值为:%d\n", sub_value);
return 0;
}
2.运行上述程序会出现下列错误:
error LNK2019: 无法解析的外部符号 _MyAdd,该符号在函数 _main 中被引用
error LNK2019: 无法解析的外部符号 _MySub,该符号在函数 _main 中被引用
3.这是因为我们生成的静态库在没有说明的情况下是按照 c++ 格式进行生成的,因此 int Add 函数并不会是 Add 函数,而是类似 Add%¥& 这样的函数,而这是 C 语言格式所找不到的,因此会出现找不到函数定义的情况;
4.我们使用 extern “C” 作为关键字添加到 int Add 函数及函数 int Sub 之前即可解决 .c 文件无法调用的问题,但同时, .cpp 文件无法调用上述过程所生成的静态库文件;
5.我们在 .h 文件中使用 #ifdef 来解决该问题;
.h 头文件
#ifndef _MYSTATICLIB_H_
#define _MYSTATICLIB_H_
// 为了让生成的库文件进行向下兼容,我们需要 #ifdef
// 如果不添加下述的代码,我们则不能在 .c 文件中调用 lib 库
#ifdef __cplusplus
extern "C" int MyAdd(int a, int b);
extern "C" int MySub(int a, int b);
#else
int MyAdd(int a, int b);
int MySub(int a, int b);
#endif // __cplusplus
#endif //_MYSTATICLIB_H_
.cpp 源文件
#include "MyStaticLib.h"
int MyAdd(int a, int b)
{
return a+b;
}
int MySub(int a, int b)
{
return a-b;
}
- __cplusplus 表示若我们的源文件是 .cpp 格式,那么是按照 c++ 风格进行生成库文件的,我们需要生成的库向下兼容,因此我们当使用 c++ 风格进行生成库时,我们需要将其转换为 c 风格的,需要更改 .h 头文件中的代码;
#ifndef _MYSTATICLIB_H_
#define _MYSTATICLIB_H_
// 为了让生成的库文件进行向下兼容,我们需要 #ifdef
// 如果不添加下述的代码,我们则不能在 .c 文件中调用 lib 库
#ifdef __cplusplus
extern "C" int MyAdd(int a, int b);
extern "C" int MySub(int a, int b);
#else
int MyAdd(int a, int b);
int MySub(int a, int b);
#endif // __cplusplus
#endif //_MYSTATICLIB_H_
7.这样我们生成的静态库就即支持 c 文件,也支持 cpp 文件了。
2.4 动态库
1.动态库又称动态链接库英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL还有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。DLL 是一个包含可由多个程序同时使用的代码和数据的库。
2.创建一个动态库;
3.创建一个空的解决方案,在其中添加一个win32控制台应用程序 MyDllLib ,点击下一步后选择 dll 选项,空文件;
4.在项目 MyDllLib 中,添加一个头文件和一个源文件;
.h 头文件
#ifndef _MYDLLLIB_H_
#define _MYDLLLIB_H_
int MyAdd(int a, int b);
int MySub(int a, int b);
#endif//_MYDLLLIB_H_
.cpp 源文件
int MyAdd(int a, int b)
{
return a+b;
}
int MySub(int a, int b)
{
return a-b;
}
5.我们生成解决方案,发现在 Debug 文件夹下有一个 .dll 的文件 MyDllLib.dll ,这时我们就生成了一个 动态库 ,但是我们无法调用这个库,我们需要给其添加一个动态库导出关键字,更改 .cpp 文件:
_declspec(dllexport) int MyAdd(int a, int b)
{
return a+b;
}
_declspec(dllexport) int MySub(int a, int b)
{
return a-b;
}
6.此时我们就可以在 main.cpp 文件中调用函数 Add 和 Sub 了;
7.我们也可以导入库,更改 .h 头文件与 .cpp 源文件;
.h 头文件
#ifndef _MYDLLLIB_H_
#define _MYDLLLIB_H_
#ifndef EXPORT
#define EXPORT _declspec(dllimport)
#endif //EXPORT
EXPORT int MyAdd(int a, int b);
EXPORT int MySub(int a, int b);
#endif//_MYDLLLIB_H_
.cpp 源文件
#define EXPORT _declspec(dllexport)
#include "MyDllLib.h"
_declspec(dllexport) int MyAdd(int a, int b)
{
return a+b;
}
_declspec(dllexport) int MySub(int a, int b)
{
return a-b;
}
8.我们更改头文件将生成的动态库可以让 .c 文件也可以调用;更改 .h 文件;
#ifndef _MYDLLLIB_H_
#define _MYDLLLIB_H_
#ifdef __cplusplus
#ifndef EXPORT
#define EXPORT _declspec(dllimport)
#endif //EXPORT
extern "C" EXPORT int MyAdd(int a, int b);
extern "C" EXPORT int MySub(int a, int b);
#else
#ifndef EXPORT
#define EXPORT _declspec(dllimport)
#endif //EXPORT
EXPORT int MyAdd(int a, int b);
EXPORT int MySub(int a, int b);
#endif // __cplusplus
#endif//_MYDLLLIB_H_