进程与线程、多线程

  之前写过一个相关的学习记录文章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

猜你喜欢

转载自blog.csdn.net/qq_21049875/article/details/79589126