精华内容
下载资源
问答
  • 多线程编程

    千次阅读 2017-09-07 10:22:24
    第一题:线程的基本概念、线程的基本状态及状态之间的关系?   线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源。 只拥有一点儿在运行中不可少的资源,但它可与同属一个进程...

    第一题:线程的基本概念、线程的基本状态及状态之间的关系?

     

    线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源。

    只拥有一点儿在运行中不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。

    线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。

     

    1.新建状态(New):

            当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。当一个线程处于新生状态时,程序还没有开始运行线程中的代码

     

    2.就绪状态(Runnable)

           一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

            处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(threadscheduler)来调度的。

     

     

    第二题:线程与进程的区别?

    1.概念       

    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

     

    线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

     

    2.关系

    一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

     

    相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

     

    3.区别

    进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

     

    1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

    2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

    3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

    4)线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

    5)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

     

    4.优缺点

    线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。


    多进程编程补充:

    Linux系统中,可以使用fork()函数开启新的进程

    fork()函数,如果是子进程,返回0,如果是父进程,返回子进程id

    fork函数内涵:http://blog.csdn.net/xy010902100449/article/details/44851453

    1:在理解时,你可以认为fork后,这两个相同的虚拟地址指向是不同的物理地址,这样方便理解父子进程之间的独立性

    2:但实际上,linux为了提高fork 的效率,采用了 copy-on-write技术,fork后,这两个虚拟地址实际上指向相同的物理地址(内存页),只有任何一个进程试图修改这个虚拟地址里的内容前,两个虚拟地址才会指向不同的物理地址(新的物理地址的内容从原物理地址中复制得到)

     

    Linux系统中:采用fork()、exec()类函数(execve函数),wait()函数调用新的进程而不kill旧进程。

    Windows下采用类似方法,父进程等待子进程

     

    Linux下的进程间通信:(http://blog.csdn.net/wallwind/article/details/6899330

    1.通道:无名通道:pipe(),有名通道:mknodmyfifo p命令,   mkfifo(“myfifo”,“rw”)函数

    2.消息队列

    3.内存共享

    http://blog.chinaunix.net/uid-26833883-id-3230564.html


    展开全文
  • 多线程编程

    千次阅读 2005-05-03 01:24:00
    多线程编程☆ 关于多线程的一些细节☆ 用VC实现多线程的调度和处理☆ 用VC实现多线程☆ 多线程编程技术及其实现☆ C#的多线程 机制探索☆ C#多线程编程实例实战 ☆ DELPHI下的多线程程序设计☆ VC++环境下利用管道和...
    展开全文
  • java多线程编程实例

    万次阅读 多人点赞 2018-05-25 10:01:22
    这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下。1.相关知识:Java多线程程序设计到的知识:(1)对同一个数量...

            这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下。

    1.三个售票窗口同时出售20张票

    程序分析:
        (1)票数要使用同一个静态值
        (2)为保证不会出现卖出同一个票数,要java多线程同步锁。
    设计思路:
        (1)创建一个站台类Station,继承Thread,重写run方法,在run方法里面执行售票操作!售票要使用同步锁:即有一个站台卖这张票时,其他站台要等这张票卖完!
        (2)创建主方法调用类

    (一)创建一个站台类,继承Thread

    package com.xykj.threadStation;
    public class Station extends Thread {
        // 通过构造方法给线程名字赋值
        public Station(String name) {
           super(name);// 给线程名字赋值
        }
        // 为了保持票数的一致,票数要静态
        static int tick = 20;
        // 创建一个静态钥匙
        static Object ob = "aa";//值是任意的
        // 重写run方法,实现买票操作
        @Override
        public void run() {
          while (tick > 0) {
            synchronized (ob) {// 这个很重要,必须使用一个锁,
              // 进去的人会把钥匙拿在手上,出来后才把钥匙拿让出来
              if (tick > 0) {
                System.out.println(getName() + "卖出了第" + tick + "张票");
                tick--;
              } else {
                System.out.println("票卖完了");
              }
            }
            try {
               sleep(1000);//休息一秒
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
      }
    }

    (二)创建主方法调用类

    package com.xykj.threadStation;
    public class MainClass {
      /**
       * java多线程同步锁的使用
       * 示例:三个售票窗口同时出售10张票
       * */
      public static void main(String[] args) {
        //实例化站台对象,并为每一个站台取名字
         Station station1=new Station("窗口1");
         Station station2=new Station("窗口2");
         Station station3=new Station("窗口3");
        // 让每一个站台对象各自开始工作
         station1.start();
         station2.start();
         station3.start();
      }
    }

    程序运行结果:

    窗口1卖出了第20张票
    窗口2卖出了第19张票
    窗口3卖出了第18张票
    窗口3卖出了第17张票
    窗口1卖出了第16张票
    窗口2卖出了第15张票
    窗口3卖出了第14张票
    窗口1卖出了第13张票
    窗口2卖出了第12张票
    窗口2卖出了第11张票
    窗口1卖出了第10张票
    窗口3卖出了第9张票
    窗口3卖出了第8张票
    窗口1卖出了第7张票
    窗口2卖出了第6张票
    窗口3卖出了第5张票
    窗口1卖出了第4张票
    窗口2卖出了第3张票
    窗口3卖出了第2张票
    窗口1卖出了第1张票
    票卖完了

    可以看到票数是不会有错的!

    2.两个人AB通过一个账户A在柜台取钱和B在ATM机取钱!

    程序分析:
            钱的数量要设置成一个静态的变量,两个人要取的同一个对象值。
    (一)创建一个Bank类
    package com.thread.demo.demo2;
    
    import java.util.Objects;
    
    public class Bank {
    	// 假设一个账户有1000块钱  
    	static double money = 1000;
    	// 柜台Counter取钱的方法  
    	private void Counter(double money) {
    		Bank.money -= money;
    		System.out.println("柜台取钱" + money + "元,还剩" + Bank.money + "元!");
    	}
    	// ATM取钱的方法  
    	private void ATM(double money) {
    		Bank.money -= money;
    		System.out.println("ATM取钱" + money + "元,还剩" + Bank.money + "元!");
    	}
    	
    	//提供一个对外取款途径,防止直接调取方法同时取款时,并发余额显示错误
    	public synchronized void outMoney(double money, String mode) throws Exception{
    		if(money > Bank.money){
    			//校验余额是否充足
    			throw new Exception("取款金额"+money+",余额只剩"+Bank.money+",取款失败");
    		}
    		if(Objects.equals(mode, "ATM")){
    			ATM(money);
    		} else {
    			Counter(money);
    		}
    	}
    
    }

    (二)创建一个PersonA类

    package com.thread.demo.demo2;
    
    public class PersonA extends Thread {
    
    	Bank bank;
    	
    	String mode;
    
    	public PersonA(Bank bank, String mode) {
    		this.mode = mode;
    		this.bank = bank;
    	}
    
    	public void run (){
    		while(bank.money >= 100){
    			try {
    				bank.outMoney(100, mode);
    			} catch (Exception e1) {
    				// TODO Auto-generated catch block
    				e1.printStackTrace();
    			}
    			try {
    				sleep(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    }

    (三)创建一个PersonB类

    package com.thread.demo.demo2;
    
    public class PersonB extends Thread {
    	Bank bank;
    	
    	String mode;
    
    	public PersonB(Bank bank, String mode) {
    		this.bank = bank;
    		this.mode = mode;
    	}
    
    	public void run() {
    		while (bank.money >= 200) {
    			try {
    				bank.outMoney(200, mode);
    			} catch (Exception e1) {
    				// TODO Auto-generated catch block
    				e1.printStackTrace();
    			}
    			try {
    				sleep(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		
    	}
    }

    (四)创建主方法的调用类

    package com.thread.demo.demo2;
    
    /**
     * 两个人AB通过一个账户A在柜台取钱和B在ATM机取钱
     * */
    public class MainClass {
    	public static void main(String[] args) {
    		Bank bank = new Bank();
    		// 实例化两个人,传入同一个银行的对象
    		PersonA a = new PersonA(bank, "Counter");
    		PersonB b = new PersonB(bank, "ATM");
    		a.start();
    		b.start();
    	}
    }
    运行结果:

    可以看到取完就停止运行了。

    3.龟兔赛跑问题

    龟兔赛跑:2000米 
    要求:
        (1)兔子每 0.1 秒 5 米的速度,每跑20米休息1秒;
        (2)乌龟每 0.1 秒跑 2 米,不休息;
        (3)其中一个跑到终点后另一个不跑了!
    程序设计思路:
        (1)创建一个Animal动物类,继承Thread,编写一个running抽象方法,重写run方法,把running方法在run方法里面调用。
        (2)创建Rabbit兔子类和Tortoise乌龟类,继承动物类
        (3)两个子类重写running方法
        (4)本题的第3个要求涉及到线程回调。需要在动物类创建一个回调接口,创建一个回调对象。

    (一)创建Animal动物类

    package com.thread.demo.demo3;
    
    public abstract class Animal extends Thread {
    	public int length = 2000;// 比赛长度
    
    	public abstract void runing();
    
    	@Override
    	public void run() {
    		super.run();
    		while (length > 0) {
    			runing();
    		}
    	}
    
    	// 在需要回调数据的地方(两个子类需要),声明一个接口
    	public static interface Calltoback {
    		public void win();
    	}
    
    	// 2.创建接口对象
    	public Calltoback calltoback;
    
    }

    (二)创建Rabbit兔子类

    package com.thread.demo.demo3;
    
    public class Rabbit extends Animal {
    
    	public Rabbit() {
    		setName("兔子");
    	}
    
    	@Override
    	public void runing() {
    		//兔子速度
    		int dis = 5;
    		length -= dis;
    
    		System.out.println("兔子跑了" + dis + "米,距离终点还有" + length + "米");
    		if (length <= 0) {
    			length = 0;
    			System.out.println("兔子获得了胜利");
    			// 给回调对象赋值,让乌龟不要再跑了
    			if (calltoback != null) {
    				calltoback.win();
    			}
    		}
    
    		try {
    			if ((2000 - length) % 20 == 0) {	// 每20米休息一次,休息时间是1秒
    				sleep(1000);
    			} else {				//没0.1秒跑5米
    				sleep(100);
    			}
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }

    (三)创建Tortoise乌龟类

    package com.thread.demo.demo3;
    
    public class Tortoise extends Animal {
    	public Tortoise() {
    		setName("乌龟");// Thread的方法,给线程赋值名字
    	}
    
    	// 重写running方法,编写乌龟的奔跑操作
    	@Override
    	public void runing() {
    		// 乌龟速度
    		int dis = 2;
    		length -= dis;
    		System.out.println("乌龟跑了" + dis + "米,距离终点还有" + length + "米");
    		if (length <= 0) {
    			length = 0;
    			System.out.println("乌龟获得了胜利");
    			// 让兔子不要在跑了
    			if (calltoback != null) {
    				calltoback.win();
    			}
    		}
    		try {
    			sleep(100);						//没0.1秒跑2米
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }

    (四)创建一个让动物线程停止的类,这里要实现回调接口

    package com.thread.demo.demo3;
    
    import com.thread.demo.demo3.Animal.Calltoback;
    
    public class LetOneStop implements Calltoback {
    	// 动物对象
    	Animal an;
    
    	// 获取动物对象,可以传入兔子或乌龟的实例
    	public LetOneStop(Animal an) {
    		this.an = an;
    	}
    
    	// 让动物的线程停止
    	@Override
    	public void win() {
    		// 线程停止
    		an.stop();
    	}
    }

    (五)创建一个主方法调用类

    package com.thread.demo.demo3;
    
    public class MainClass {
    	/**
    	 * 龟兔赛跑:2000米
    	 */
    	public static void main(String[] args) {
    		// 实例化乌龟和兔子
    		Tortoise tortoise = new Tortoise();
    		Rabbit rabbit = new Rabbit();
    		// 回调方法的使用,谁先调用calltoback方法,另一个就不跑了
    		LetOneStop letOneStop1 = new LetOneStop(tortoise);
    		// 让兔子的回调方法里面存在乌龟对象的值,可以把乌龟stop
    		rabbit.calltoback = letOneStop1;
    		LetOneStop letOneStop2 = new LetOneStop(rabbit);
    		// 让乌龟的回调方法里面存在兔子对象的值,可以把兔子stop
    		tortoise.calltoback = letOneStop2;
    		// 开始跑
    		tortoise.start();
    		rabbit.start();
    	}
    
    }

    运行结果:


    4. 线程示例总结

        (1)代码块锁是一个防止数据发生错误的一个重要手段;

        (2)对象的统一性是非常重要的,这要想到对象的传入问题,要操作的对象只能new一次,其他的操作都是对这个传入的对象进行的,才能保证数据一致性,完整性和正确性。

    展开全文
  • 线程与进程相比,它是一种花销小,...多编程并发在企业中开发显得尤为重要,本课程包含Windows多线程编程与C++11高并发编程,通过浅显易懂的代码与讲解,让你的多线程编程能力得到质的飞跃,具备开发高并发代码的能力!
  • 多线程编程

    一、多线程简介

    1、什么是线程?

           线程在操作系统原理中是这样描述的:线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。
    在这里插入图片描述

    2、主线程和子线程

           一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程),C/C++程序中,主线程就是通过main函数进入的线程,由主线程调用pthread_create()创建的线程称为子线程,子线程也可以有自己的入口函数,该函数由用户在创建的时候指定。每个线程都有自己的线程ID,可以通过pthread_self()函数获取。最常见的线程模型中,除主线程较为特殊之外,其他线程一旦被创建,相互之间就是对等关系,不存在隐含的层次关系。每个进程可创建的最大线程数由具体实现决定。

           无论在windows中还是Posix中,主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系

    1. 可会合(joinable):这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。
    2. 相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,这种方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。

           线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

    3、创建子线程

    pthread_create()函数

    函数原型:

    #include <pthread.h>
    
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void
    *arg);
    

    参数介绍:

    • 第一个参数thread是一个pthread_t类型的指针,它用来返回该线程的线程ID。每个线程都能够通过pthread_self()来获取自己的线程ID(pthread_t类型)
    • 第二个参数是线程的属性attr,其类型是 pthread_attr_t 结构体类型,其定义如下:
    typedef struct
    {
    	int 					detachstate; 			//线程的分离状态
    	int 					schedpolicy; 			//线程调度策略
    	struct sched_param 		schedparam; 			//线程的调度参数
    	int						inheritsched; 			//线程的继承性
    	int 					scope; 					//线程的作用域
    	size_t 					guardsize;				//线程栈末尾的警戒缓冲区大小
    	int 					stackaddr_set;
    	void 				   *stackaddr; 				//线程栈的位置
    	size_t 					stacksize; 				//线程栈的大小
    }pthread_attr_t;
    
    /*
        对于这些属性,我们需要设定的是线程的分离状态,如果有必要,也需修改每个线程的栈大小。
    每个线程创建后默认是joinable状态,该状态需要主线程调用 pthread_join 等待它退出,否则,
    子线程在结束时,内存资源不能得到释放造成内存泄漏。所以我们创建线程时一般会将线程设置为
    分离状态,具体有两种方法:
    	1. 线程里面调用 pthread_detach(pthread_self()) 函数,这个方法最简单
    	2. 在创建线程的属性设置里设置PTHREAD_CREATE_DETACHED属性
     */
    
    • 第三个参数start_routine是一个函数指针,它指向的函数原型是 void *func(void *),这是所创建的子线程要执行的任务(回调函数,返回值是void *类型,形参是void *)
    • 第四个参数arg是传给所调用的函数的参数,如果有多个参数要传递的话,就需要将这多个参数封装到一个结构体中,再传入函数中;

    下面以一个程序演示子线程的创建
    代码如下:

    1  #include <stdio.h>
    2  #include <string.h>
    3  #include <errno.h>
    4  #include <stdlib.h>
    5  #include <unistd.h>
    6  #include <pthread.h>
    7
    8  void *thread_worker1(void *args);
    9  void *thread_worker2(void *args);
    10
    11 int main(int argc, char *argv[])
    12 {
    13      int 				shared_var = 1000;
    14	    pthread_t 			tid;
    15 		pthread_attr_t 		thread_attr;
    16
    17
    18 		if (pthread_attr_init(&thread_attr))
    19		{
    20 			printf("pthread_attr_init() failure: %s\n", strerror(errno));
    21 			return -1;
    22		}
    23
    24 		if (pthread_attr_setstacksize(&thread_attr, 120*1024))	//重新设置子线程栈大小
    25 		{
    26 			printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
    27 			return -1;
    28		}
    29		//设置子线程与主线程为相分离的关系
    30		if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
    31 		{
    32 			printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
    33 			return -1;
    34 		}
    35		//创建第一个子线程,去执行thread_worker1()
    36 		pthread_create(&tid, &thread_attr, thread_worker1, &shared_var);
    37 		printf("Thread worker1 tid[%ld] created ok\n", tid);
    38		//创建第二个子线程,去执行thread_worker2()
    39		pthread_create(&tid, NULL, thread_worker2, &shared_var);
    40 		printf("Thread worker2 tid[%ld] created ok\n", tid);
    41
    42 		pthread_attr_destroy(&thread_attr);		//销毁为线程重新设置的属性
    43
    44 		/* 第二个子线程默认是joinable,在这里阻塞,等待与子线程会合 */
    45 		pthread_join(tid, NULL);
    46
    47
    48 		while (1)
    49 		{
    50 			printf("Main/Control thread shared_var: %d\n", shared_var);
    51 			sleep(10);
    52 		}
    53 }
    54 
    55 void *thread_worker1(void *args)
    56 {
    57 		int *ptr = (int *)args;
    58
    59 		if (!args)
    60 		{
    61 			printf("%s() get invalid arguments\n", __FUNCTION__);
    62 			pthread_exit(NULL);
    63		}
    64
    65 		printf("Thread workder 1 [%ld] start running...\n", pthread_self());
    66
    67 		while (1)
    68 		{
    69 			printf("+++: %s before shared_var++: %d\n", __FUNCTION__, *ptr);
    70 			*ptr += 1;
    71 			sleep(2);
    72 			printf("+++: %s after sleep shared_var: %d\n", __FUNCTION__, *ptr);
    73 		}
    74
    75 		printf("Thread workder 1 exit...\n");
    76
    77 		return NULL;
    78 }
    79 //宏__FUNCTION__用来获取函数名
    80 void *thread_worker2(void *args)
    81 {
    82 		int *ptr = (int *)args;
    83
    84 		if (!args)
    85 		{
    86 			printf("%s() get invalid arguments\n", __FUNCTION__);
    87 			pthread_exit(NULL);
    88 		}
    89
    90 		printf("Thread workder 2 [%ld] start running...\n", pthread_self());
    91
    92 		while (1)
    93 		{
    94 			printf("---: %s before shared_var++: %d\n", __FUNCTION__, *ptr);
    95 			*ptr += 1;
    96 			sleep(2);
    97 			printf("---: %s after sleep shared_var: %d\n", __FUNCTION__, *ptr);
    98 		}
    99
    100 	printf("Thread workder 2 exit...\n");
    101
    102 	return NULL;
    103 }
    

    运行结果:

    Thread worker1 tid[1993757808] created ok
    Thread workder 1 [1993757808] start running...
    +++: thread_worker1 before shared_var++: 1000
    Thread worker2 tid[1993634928] created ok
    Thread workder 2 [1993634928] start running...
    ---: thread_worker2 before shared_var++: 1001
    +++: thread_worker1 after sleep shared_var: 1002
    +++: thread_worker1 before shared_var++: 1002
    ---: thread_worker2 after sleep shared_var: 1002
    ---: thread_worker2 before shared_var++: 1003
    +++: thread_worker1 after sleep shared_var: 1004
    +++: thread_worker1 before shared_var++: 1004
    ---: thread_worker2 after sleep shared_var: 1005
    ---: thread_worker2 before shared_var++: 1005
    

    程序分析:

    • 36行和39行调用pthread_create()函数用来创建了两个子线程;
    • 代码15行我们定义了创建线程的属性变量thread_attr ,在对该属性进行设置前,我们需要先调用pthread_attr_init 函数初始化它(第18行),在第24行我们设置线程的栈大小为120K,同时在第30行设置线程的属性为分离状态。第36行创建线程时使用了该属性创建线程,这时创建的子进程就是分离状态了。线程属性在使用完之后,需调用pthread_attr_destroy (第45行)把它摧毁释放;
    • 而代码39行创建子线程时并没有使用该线程,同时在thread_worker2() 里并没有调用pthread_detach()将线程设置为分离状态。这时就需要主线程在45行处调用pthread_join() 等待第二个子线程退出。因此主线程也就阻塞在这里,从而不会往下继续执行;
    • 在创建两个线程时,我们都通过第四个参数将主线程栈中的 shared_var 变量地址传给了子线程,因为所有线程都是在同一进程空间中运行,而只是子线程有自己独立的栈空间,所以这时所有子线程都可以访问主线程空间的shared_var变量。

    注:对于多线程编程在编译时,一定要加上-lpthread 选项告诉链接器在链接的时候要连接pthread库

    4、锁的概念

           上面的程序存在着一定的问题!两个子线程操纵了同一个变量shared_var ,那么两个线程都对变量shared_var 进行修改的话,可能会产生数据的不一致!因此会看到第一个线程只是对变量shared_var 进行加1操作,但下次打印确实102,也就是加了2。接下来,引入锁的概念。

    ① 互斥锁
           试想一下,我们寝室只有一个洗手间,那多个人是怎么解决洗漱台共享的问题?那么,这时就要引入锁的机制!在这里洗漱台就是临界资源,我们在进入到洗手间(临界区)后,就首先将洗手间上锁; 然后用完离开洗手间(临界区)之后,把锁打开以供别人使用。如果有人想去洗手间时发现门锁上了,他也有两种方法:

    • 1、在洗手间门口等(阻塞模式);
    • 2、暂时先离开,不过过会儿再过来看(非阻塞模式);

           那么对于上面的程序,变量shared_var就是临界资源,操作该变量的那段代码就是临界区。那我们把代码修改一下,通过锁的机制解决共享资源的问题:

    1  #include <stdio.h>
    2  #include <string.h>
    3  #include <errno.h>
    4  #include <stdlib.h>
    5  #include <unistd.h>
    6  #include <pthread.h>7
    8  void *thread_worker1(void *args);
    9  void *thread_worker2(void *args);
    10 //由于要传两个参数,所以这里定义了一个结构体
    11 typedef struct worker_ctx_s
    12 {
    13 		int 				shared_var;
    14 		pthread_mutex_t 	lock;	//引入锁
    15 }worker_ctx_t;
    16
    17 int main(int argc, char **argv)
    18 {
    19 		worker_ctx_t 		worker_ctx;
    20 		pthread_t			tid;
    21 		pthread_attr_t 		thread_attr;
    22
    23 		worker_ctx.shared_var = 1000;
    24 		pthread_mutex_init(&worker_ctx.lock, NULL);
    25		//初始化互斥锁
    26
    27 		if (pthread_attr_init(&thread_attr))
    28 		{
    29 			printf("pthread_attr_init() failure: %s\n", strerror(errno));
    30 			return -1;
    31 		}
    32
    33 		if (pthread_attr_setstacksize(&thread_attr, 120*1024))
    34 		{
    35 			printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
    36 			return -1;
    37 		}
    38
    39 		if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
    40 		{
    41 			printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
    42 			return -1;
    43 		}
    44
    45 		pthread_create(&tid, &thread_attr, thread_worker1, &worker_ctx);
    46 		printf("Thread worker1 tid[%ld] created ok\n", tid);
    47
    48 		pthread_create(&tid, &thread_attr, thread_worker2, &worker_ctx);
    49 		printf("Thread worker2 tid[%ld] created ok\n", tid);
    50
    51 		while (1)
    52 		{
    53 			printf("Main/Control thread shared_var: %d\n", worker_ctx.shared_var);
    54 			sleep(10);
    55 		}
    56
    57 		pthread_mutex_destroy(&worker_ctx.lock);
    58 }
    59
    60 void *thread_worker1(void *args)
    61 {
    62 		worker_ctx_t *ctx = (worker_ctx_t *)args;
    63
    64 		if (!args)
    65		{
    66 			printf("%s() get invalid arguments\n", __FUNCTION__);
    67 			pthread_exit(NULL);
    68 		}
    69
    70 		printf("Thread workder 1 [%ld] start running...\n", pthread_self());
    71
    72 		while (1)
    73 		{
    74 			pthread_mutex_lock(&ctx->lock);	//设置阻塞锁
    75
    76 			printf("+++: %s before shared_var++: %d\n", __FUNCTION__, ctx->shared_var);77 ctx->shared_var ++;
    78 			sleep(2);
    79 			printf("+++: %s after sleep shared_var: %d\n", __FUNCTION__, ctx->shared_var);
    80
    81 			pthread_mutex_unlock(&ctx->lock); 	//打开阻塞锁
    82
    83 			sleep(1);
    84 		}
    85
    86 		printf("Thread workder 1 exit...\n");
    87
    88 		return NULL;
    89 }
    90
    91 void *thread_worker2(void *args)
    92 {
    93 		worker_ctx_t *ctx = (worker_ctx_t *)args;
    94
    95 		if (!args)
    96 		{
    97 			printf("%s() get invalid arguments\n", __FUNCTION__);
    98 			pthread_exit(NULL);
    99 		}
    100
    101 	printf("Thread workder 2 [%ld] start running...\n", pthread_self());
    102
    103 	while(1)
    104 	{
    105 		if (0 != pthread_mutex_trylock(&ctx->lock)) //设置非阻塞锁
    106 		{
    107 			continue;
    108 		}
    109
    110 		printf("---: %s before shared_var++: %d\n", __FUNCTION__, ctx->shared_var);
    111 		ctx->shared_var ++;
    112 		sleep(2);
    113 		printf("---: %s after sleep shared_var: %d\n", __FUNCTION__, ctx->shared_var);
    114
    115 		pthread_mutex_unlock(&ctx->lock);//打开非阻塞锁
    116
    117 		sleep(1);
    118 	}
    119
    120 	printf("Thread workder 2 exit...\n");
    121
    122 	return NULL;
    123 }
    

    程序分析:

    • 代码的11~15行:因为在创建线程给线程执行函数传参的时候只能传一个参数,而我们要传递共享的变量shared_var和它相应的互斥锁lock,所以在这里需要用结构体(worker_ctx_t, ctx: context)将它们封装在一块传进去。
    • 代码19行:使用work_ctx_t结构体类型定义了传给子线程的变量参数;
    • 代码24行:互斥锁在使用之前,需要先调用 pthread_mutex_init() 函数来初始化互斥锁;
    • 代码48行:在创建第二个线程时也设置了分离属性,这时主线程后面的while(1)循环就会执行了;
    • 代码57行:互斥锁在使用完之后,我们应该调用pthread_mutex_destroy()将他摧毁释放;
    • 代码74行: 这里调用pthread_mutex_lock() 来申请锁,这里是阻塞锁,如果锁被别的线程持有,则该函数不会返回;
    • 代码81行: 在访问临界资源(shared_var)完成退出临界区时,我们调用pthread_mutex_unlock来释放锁,这样其他线程才能再次访问;
    • 代码105行: 第二个线程我们使用pthread_mutex_trylock() 来申请锁,这里使用的是非阻塞锁;如果锁现在被别的线程占用则返回非0值,如果没有被占用则返回0
    • 代码83行、117行: 这里都要加上延时,否则一个线程拿到锁之后会一直占有该锁;另外一个线程则不能获取到锁;

    再次运行:

    Thread worker1 tid[1994032240] created ok
    Thread workder 1 [1994032240] start running...
    +++: thread_worker1 before shared_var++: 1000
    Thread worker2 tid[1993909360] created ok
    Main/Control thread shared_var: 1001
    Thread workder 2 [1993909360] start running...
    +++: thread_worker1 after sleep shared_var: 1001
    ---: thread_worker2 before shared_var++: 1001
    ---: thread_worker2 after sleep shared_var: 1002
    +++: thread_worker1 before shared_var++: 1002
    +++: thread_worker1 after sleep shared_var: 1003
    ---: thread_worker2 before shared_var++: 1003
    ---: thread_worker2 after sleep shared_var: 1004
    +++: thread_worker1 before shared_var++: 1004
    Main/Control thread shared_var: 1005
    +++: thread_worker1 after sleep shared_var: 1005
    ---: thread_worker2 before shared_var++: 1005
    

    通过引入互斥锁,就解决了数据不一致的问题。

    ② 死锁
           如果多个线程要调用多个对象,则在上锁的时候可能会出现“死锁”。举个例子: A、B两个线程会同时使用到两个共享变量m和n,同时每个变量都有自己相应的锁M和N。 这时A线程首先拿到M锁访问m,接下来他需要拿N锁来访问变量n; 而如果此时B线程拿着N锁等待着M锁的话,就造成了线程“死锁”。
    在这里插入图片描述
    死锁产生的4个必要条件:

           1、互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
           2、占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
           3、不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
           4、循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。

           当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是非常浪费系统资源以及影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了!
           由于产生死锁需要四个条件,那么,只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于互斥条件是非共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件。

    a、破坏“占有且等待”条件
           方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源。
    优点:简单易实施且安全。缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成
    资源浪费。使进程经常发生饥饿现象。
           方法2:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。

    b、破坏“不可抢占”条件
           当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作实效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。

    c、破坏“循环等待”条件
           可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程占有编号为i的资源时,那么它下一次申请资源只能申请编号大于i的资源。

    二、多线程改写服务器程序

    1、多进程并发服务器在这里插入图片描述

    程序代码如下:

    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <getopt.h>
    #include <pthread.h>
    #include <ctype.h>
    
    typedef void *(THREAD_BODY) (void *thread_arg);	
    void *thread_worker(void *ctx);
    //封装thread_start()函数,实现创建子线程的功能
    int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg);
    
    void print_usage(char *progname)
    {
    	printf("%s usage: \n", progname);
    	printf("-p(--port): sepcify server listen port.\n");
    	printf("-h(--Help): print this help information.\n");
    	
    	return ;
    }
    
    int main(int argc, char **argv)
    {
    	int 					sockfd = -1;
    	int 					rv = -1;
    	struct sockaddr_in 		servaddr;
    	struct sockaddr_in 		cliaddr;
    	socklen_t 				len;
    	int 					port = 0;
    	int 					clifd;
    	int 					ch;
    	int 					on = 1;
    	pthread_t 				tid;
    	struct option opts[] = {
    				{"port", required_argument, NULL, 'p'},
    				{"help", no_argument, NULL, 'h'},
    				{NULL, 0, NULL, 0}
    				};
    				
    	while ((ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1)
    	{
    		switch(ch)
    		{
    			case 'p':
    					port=atoi(optarg);
    					break;
    			case 'h':
    					print_usage(argv[0]);
    					return 0;
    		}
    	}
    	
    	if( !port )
    	{
    		print_usage(argv[0]);
    		return 0;
    	}
    	
    	sockfd = socket(AF_INET, SOCK_STREAM, 0);
    	if (sockfd < 0)
    	{
    		printf("Create socket failure: %s\n", strerror(errno));
    		return -1;
    	}
    	printf("Create socket[%d] successfully!\n", sockfd);
    	
    	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    	
    	memset(&servaddr, 0, sizeof(servaddr));
    	servaddr.sin_family = AF_INET;
    	servaddr.sin_port = htons(port);
    	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* 监听本机所有IP*/
    	//inet_aton("192.168.0.16", &servaddr.sin_addr); /* 监听指定ip */
    
    	rv = bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    	if (rv < 0)
    	{
    		printf("Socket[%d] bind on port[%d] failure: %s\n", sockfd, port, strerror(errno));
    		return -2;
    	}
    	
    	listen(sockfd, 13);
    	printf("Start to listen on port [%d]\n", port);
    	
    	while (1)
    	{
    		printf("Start accept new client incoming...\n");
    		
    		clifd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
    		if (clifd < 0)
    		{
    			printf("Accept new client failure: %s\n", strerror(errno));
    			continue;
    		}
    		printf("Accept new client[%s:%d] successfully\n", inet_ntoa(cliaddr.sin_addr),
    				ntohs(cliaddr.sin_port));
    				
    		/*注意,这里传入的是clifd的值,而不是clifd的地址*/
    		thread_start(&tid, thread_worker, (void *)clifd);
    	}
    	
    	close(sockfd);
    	return 0;
    }、
    
    int thread_start(pthread_t *thread_id, THREAD_BODY *thread_workbody, void *thread_arg)
    {
    	int 				rv = -1;
    	pthread_attr_t 		thread_attr;
    	
    	if (pthread_attr_init(&thread_attr))
    	{
    		printf("pthread_attr_init() failure: %s\n", strerror(errno));
    		goto CleanUp;
    	}
    	
    	if (pthread_attr_setstacksize(&thread_attr, 120*1024))
    	{
    		printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
    					goto CleanUp;
    	}
    	
    	if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
    	{
    		printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
    		goto CleanUp;
    	}
    	
    	/* Create the thread */
    	if (pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg))
    	{
    		printf("Create thread failure: %s\n", strerror(errno));
    		goto CleanUp;
    	}
    	
    	rv = 0;
    CleanUp:
    	/* Destroy the attributes of thread */
    	pthread_attr_destroy(&thread_attr);
    	return rv;
    }
    
    void *thread_worker(void *ctx)
    {
    	int 		clifd;
    	int 		rv;
    	char		buf[1024];
    	int 		i;
    	
    	if (!ctx)
    	{
    		printf("Invalid input arguments in %s()\n", __FUNCTION__);
    		pthread_exit(NULL);
    	}
    	
    	clifd = (int)ctx;
    	printf("Child thread start to commuicate with socket client...\n");
    	
    	while (1)
    	{
    		memset(buf, 0, sizeof(buf));
    		rv = read(clifd, buf, sizeof(buf));
    		if (rv < 0)
    		{
    			printf("Read data from client sockfd[%d] failure: %s and thread will exit\n", clifd,
    				 	  strerror(errno));
    			close(clifd);
    			pthread_exit(NULL);
    		}
    		else if( rv == 0)
    		{
    			printf("Socket[%d] get disconnected and thread will exit.\n", clifd);
    			close(clifd);
    			pthread_exit(NULL);
    		}
    		else if( rv > 0 )
    		{
    			printf("Read %d bytes data from Server: %s\n", rv, buf);
    		}
    		
    		/* convert letter from lowercase to uppercase */
    		for (i = 0; i < rv; i++)
    		{
    			buf[i] = toupper(buf[i]);
    		}
    		rv = write(clifd, buf, rv);
    		if (rv < 0)
    		{
    			printf("Write to client by sockfd[%d] failure: %s and thread will exit\n", clifd,
    					strerror(errno));
    			close(clifd);
    			pthread_exit(NULL);
    		}
    	}
    }
    

           接下来在windows下使用TCP socket测试工具连接并测试服务器的执行情况,我们可以发现服务器可以同时处理多个客户端的连接请求和通信,并在客户端断开时子线程退出,从而实现了服务器并发访问。

    展开全文
  • C++多线程编程

    万次阅读 多人点赞 2018-06-26 12:44:31
    C++ 11发布之前,C++并没有对多线程编程的专门支持,C++ 11通过标准库引入了对多线程的支持,大大方便了程序员的工作,本篇我们对这部分进行简要的介绍。需要说明的是,C++ 11标准库内部包裹了pthread库,因此,编译...
  • 我们进行程序开发的时候,肯定避免不了要处理并发的情况。...本文基于 Python3 讲解,Python 实现多线程编程需要借助于 threading 模块。 所以,我们要在代码中引用它。 import threading thread...
  • Java深入核心-多线程编程实战

    千人学习 2020-03-06 19:02:12
    对于一个 Java 程序员而言,能否熟练掌握多线程编程是判断他优秀与否的重要标准之一。因为并发编程是 Java 语言中最为晦涩的知识点,它涉及操作系统、内存、CPU、编程语言等多方面的基础能力,更为考验一个程序员的...
  • 谈到为什么需要多线程编程,可能需要从并发这个概念的历史来说起。 在很久以前,计算机并没有操作系统,同一个时刻他们只能执行一个单独的程序,而且这些程序直接访问所有的计算机资源。在计算机刚面世的那个年代,...
  • 多线程编程杂谈

    千次阅读 2013-07-11 18:50:20
    多线程编程
  • Java多线程编程

    万次阅读 2016-06-17 22:08:38
    Java多线程编程 Java给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一 个线程,并且每个线程定义了一个独立的执行路径。 多线程是多任务的一种特别...
  • Linux C++的多线程编程

    千次阅读 2017-03-13 14:36:35
    多线程编程
  • c 多线程编程01

    千次阅读 2018-10-21 00:53:57
    Java 老师希望我们尝试进行 Java 的多线程编程,也希望我们能够去实现一下 C 语言的多线程编程。用以体会不同编程语言间的多线程编程。借此机会,初步学习一下 C 语言的多线程编程。 第一部分主要内容如下: ...
  • Windows下多线程编程

    千次阅读 2019-03-26 17:58:25
    熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常。Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求。 进程与线程 1. ...
  • 转载自: ... java多线程-概念&amp;创建启动&amp;中断&...线程状态(多线程编程之一)java多线程同步以及线程间通信详解&amp;消费者生产者模式&amp;死锁&amp;Thread.join()(...
  • 本例来自《java多线程编程调试模式》: 题意:模拟流水线上的工人,  工人一直在流水线上作业,零件(可看作客户端发的请求)一到达,工人就开始进行工作,无零件时工作处于等待状态 具体的业务代码有详注释: ...
  • 1.章节结构图 ...多线程编程一方面有助于提高系统吞吐量、提高系统的响应性、充分利用多核处理器、最小化对系统资源的使用和简化程序的结构;另一方面面临线程安全、线程活性、上下文切换和可靠性等问题 ...
  • golang之多线程编程

    2020-09-07 16:42:12
    多线程编程时一种比多线程编程更灵活、高效的并发编程关系 go并发编程模型在底层使用的是内核提供的posix线程库 进程总结 线程 线程可以看成是进程中的控制流。 一个进程中至少包含一个线程【主线程】,进程中至少...
  • Android多线程编程(一)- 线程基础

    千次阅读 2020-12-30 15:05:41
    到了学习下一部分了,多线程编程一直都是Android经常使用的部份,在进行网络请求或者访问数据库时,为了避免主线程被耗时操作阻塞了进度,通常都会开启子线程进行处理,多线程编程在Android属于相当重要的一部分,接...
  • C++11多线程编程

    千人学习 2018-09-06 14:34:58
    本课程,讲解的重点定位在c++11新标准中的多线程开发部分,同时,老师还会结合自己的经验把多线程的讲解进一步拓展到一个比较大的范畴,因为无论是c++11多线程开发还是各种其他的多线程开发实现方法,都有很多类似的...
  • 下载 多线程编程技术开发资料 高清完整PDF版

    千次下载 热门讨论 2017-05-16 20:01:41
    多线程编程技术开发资料.pdf 个人收集电子书,仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!
  • C++多线程编程入门

    千次阅读 2017-10-25 10:51:49
    C++多线程编程入门 前言:一直想抽个时间学习下多线程编程,今天就从C++简单多线程编程入手吧,使用Windows下的API. 1、线程创建 创建和关闭线程的API函数:HANDLE CreateThread( ...
  • QT中的多线程编程

    千次阅读 2018-08-22 16:57:33
    1、Qt中的多线程编程 2、多线程间的同步 3、多线程间的互斥 1、线程锁 2、信号量 4、小结 1、Qt中的多线程编程 Qt中通过QThread直接支持多线程 - QThread是一个跨平台的多线程解决方案 - QThread以简洁...
  • Java多线程编程 深入详解

    千次阅读 多人点赞 2018-01-17 21:59:54
    《Java多线程编程 深入详解》读书笔记 第一章 多进程多线程概述 线程概述 进程(PROCESS):CPU的执行路径 多进程:系统中同时运行多个程序 线程(THREAD):运行在进程当中的运行单元 多线程...
  • C++多线程编程(真实入门!)

    万次阅读 多人点赞 2019-04-23 18:24:35
    5分钟学会简单的C++多线程编程前言线程教程创建一个线程:CreateThread()创建一个带参线程:利用lpParameter参数等待指定线程结束。 前言 本篇博客使用的库:<windows.h> 提供的线程api(当然是使用操作系统...
  • 理解c++多线程编程

    千次阅读 多人点赞 2018-09-30 14:56:28
    多线程编程 工欲善其事必先利其器CLion安装mingw并配置以支持c++11多线程编程 本篇博文不是主要介绍互斥锁之类的,是理解线程的执行,以便以后有把握的写多线程程序。 #include&amp;amp;amp;lt;thread&amp...
  • c++多线程编程

    万次阅读 多人点赞 2012-09-25 10:01:01
    一直对多线程编程这一块很陌生,决定花一点时间整理一下。 os:ubuntu 10.04 c++ 1.最基础,进程同时创建5个线程,各自调用同一个函数 #include #include //多线程相关操作头文件,可移植众多平台 using ...
  • VC++多线程编程

    千次阅读 2018-07-21 14:14:29
    十个例子清晰列举啦多线程编程的奥妙。 VC中多线程使用比较广泛而且实用,在网上看到的教程.感觉写的挺好. 一、问题的提出 编写一个耗时的单线程程序:  新建一个基于对话框的应用程序SingleThread,在主对话框...
  • Java多线程编程-(8)-多图深入分析ThreadLocal原理

    万次阅读 多人点赞 2017-10-20 17:15:13
    前几篇:Java多线程编程-(1)-线程安全和锁Synchronized概念Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性Java多线程编程-(3)-线程本地ThreadLocal的介绍与使用Java多线程编程-(4)-线程间通信...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 76,626
精华内容 30,650
关键字:

多线程编程