之前写过一个相关的学习记录文章http://blog.csdn.net/qq_21049875/article/details/78289277。
然后没坚持下去,并发,多线程就只了解了一丁点内容,长时间没使用便忘了很多概念。
进程,是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
线程,则是程序执行流的最小单元,由进程给其分配资源。
进程有独立的地址空间,而同一进程中的线程共享该进程的地址空间。比如在linux下面启动一个新的进程,系统必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种非常昂贵的多任务工作方式。而运行一个进程中的线程,它们之间共享大部分数据,使用相同的地址空间,因此启动一个线程,切换一个线程远比进程操作要快,花费也要小得多。当然,线程是拥有自己的寄存器和堆栈(线程栈),比如在windows中_beginthreadex创建一个新进程就会在调用CreateThread的同时申请一个专属于线程的数据块(_tiddata)。此外,线程有自己线程栈。
这样做的原因:
(1)stack is for local/method variables; heap is for instance/class variable
(2)Stack常常用来存放 函数的参数,函数中使用的自动变量,存放过程活动记录;如果多个线程共享一个Stack
会非常的凌乱,不方便使用
(3)使用共享Heap的目的是为了高效的数据共享
线程之间的通信比较方便。统一进程下的线程共享数据(比如全局变量,静态变量,堆,子进程),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。在一个线程中分配的堆在各个线程中均可以使用,在一个线程中打开的文件各个线程均可用,当然指同一进程中的线程。
多进程比多线程程序要健壮。一个线程死掉整个进程就死掉了,但是在保护模式下,一个进程死掉对另一个进程没有直接影响。
线程的执行与进程是有区别的。每个独立的线程有有自己的一个程序入口,顺序执行序列和程序的出口,但是线程不能独立执行,必须依附与程序之中,由应用程序提供多个线程的并发控制。
linux中进程具有父子关系,形成进程树,但是线程是平等的没有父子关系。
测试代码,每个线程所使用的栈是不同的:
int a = 5;
void t_func() {
//int a=0;
while (a < 20) {
cout << std::this_thread::get_id()<<": "<<a << endl;
a++;
}
}
int main()
{
thread f1(t_func), f2(t_func);
f1.join();
f2.join();
return 0;
}
可以发现我们创建的两个线程,在调用同一个函数,而函数中修改的是全局变量a,所以a在两个线程执行的函数中不断增加。
当我们修改a为局部变量的时候(如下图),则两个线程所修改的a是不同的。
死锁
推荐看这篇文章,里面图文并茂,很详细,http://blog.csdn.net/shanghairuoxiao/article/details/70444940
多线程BFS
在使用C++的多线程thread之后,感觉能够使用多线程在爬虫上,于是测试了一下多线程BFS。
于是乎写了一个二叉树,想让程序去遍历该二叉树,然后开两个线程去执行下面的work函数,也就是遍历,但是发现会因为程序执行太快..初始队列只有一个元素,元素为空就结束了,所以就一个线程在运行,只有在队列元素多的情况下,多线程才跑起来=_=,想了想好像没必要开多线程,示例代码先粘贴在这里吧,觉得多线程程序设计有些迷迷糊糊,原子操作什么的都没接触,就比如:
以下多线程对int型变量x的操作,哪几个不需要进行同步:
A. x=y; B. x++; C. ++x; D. x=1;
答案是:D,因为A、B、C,都要涉及到读取y、x的值,然后再写,而D则不用读,直接写,原子操作。
struct testTree {
testTree *lchild;
testTree *rchild;
int val;
testTree() :lchild(nullptr), rchild(nullptr), val(0) {}
};
testTree* buildTree(int *arr,int len,int pos) {
int tpos1 = 2 * pos + 1, tpos2 = 2 * pos + 2;
auto node = new testTree();
node->val = arr[pos];
if (tpos1 > len - 1) {
return node;
}
if (tpos1 <= len - 1) {
node->lchild = buildTree(arr, len, tpos1);
}
if (tpos2 <= len - 1) {
node->rchild = buildTree(arr, len, tpos2);
}
return node;
}
void destroy(testTree **tree) {
if ((*tree) != nullptr) {
if ((*tree)->lchild == nullptr && (*tree)->rchild == nullptr) {
delete *tree;
*tree = nullptr;
}
else {
destroy(&(*tree)->lchild);
destroy(&(*tree)->rchild);
}
}
}
mutex t_mutex;
void t_pop(queue<testTree*> &tmp) {
lock_guard<mutex>lock(t_mutex);
if (!tmp.empty())
tmp.pop();
}
void t_push(queue<testTree*> &tmp, testTree* val) {
lock_guard<mutex>lock(t_mutex);
tmp.push(val);
}
auto t_front(queue<testTree*> &tmp) {
lock_guard<mutex>lock(t_mutex);
if (!tmp.empty())
return tmp.front();
}
void work(queue<testTree*> &m_queue) {
while (!m_queue.empty()) {
auto curr = t_front(m_queue);
t_pop(m_queue);
cout << "current thread : " << std::this_thread::get_id() << endl;
cout << "curr val : " << curr->val;
if (curr->lchild != nullptr) {
t_push(m_queue, curr->lchild);
cout << "push val : " << curr->lchild->val;
}
if (curr->rchild != nullptr) {
t_push(m_queue, curr->rchild);
cout << "push val : " << curr->rchild->val;
}
}
}
int main()
{
clock_t start, finish;
int a[] = { 3,2,4,8,9,7,1};
int len = sizeof(a) / sizeof(a[0]);
int test_val = 0;
auto tree = buildTree(a, len, 0);
queue<testTree*> m_queue;
m_queue.push(tree);
start = clock();
thread t1(work, std::ref(m_queue)) , t2(work, std::ref(m_queue));
t1.join(), t2.join();
finish = clock();
destroy(&tree);
double totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "\n此程序的运行时间为" << totaltime << "秒!" << endl;
return 0;
}
临界区,互斥量,信号量,事件的区别
推荐了解一下:ttps://blog.csdn.net/bao_qibiao/article/details/4516196