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

    千次阅读 2005-05-03 01:24:00
    多线程编程☆ 关于多线程的一些细节☆ 用VC实现多线程的调度和处理☆ 用VC实现多线程☆ 多线程编程技术及其实现☆ C#的多线程 机制探索☆ C#多线程编程实例实战 ☆ DELPHI下的多线程程序设计☆ VC++环境下利用管道和...
    展开全文
  • 我们进行程序开发的时候,肯定避免不了要处理并发的情况。...本文基于 Python3 讲解,Python 实现多线程编程需要借助于 threading 模块。 所以,我们要在代码中引用它。 import threading thread...

    我们进行程序开发的时候,肯定避免不了要处理并发的情况。

    一般并发的手段有采用多进程和多线程。

    但线程比进程更轻量化,系统开销一般也更低,所以大家更倾向于用多线程的方式处理并发的情况。

    Python 提供多线程编程的方式。

    本文基于 Python3 讲解,Python 实现多线程编程需要借助于 threading 模块。

    所以,我们要在代码中引用它。

    import threading
    

    threading 模块中最核心的内容是 Thread 这个类。

    我们要创建 Thread 对象,然后让它们运行,每个 Thread 对象代表一个线程,在每个线程中我们可以让程序处理不同的任务,这就是多线程编程。

    值得注意的是,程序运行时默认就是在主线程上

    创建 Thread 对象有 2 种手段。

    1. 直接创建 Thread ,将一个 callable 对象从类的构造器传递进去,这个 callable 就是回调函数,用来处理任务。
    2. 编写一个自定义类继承 Thread,然后复写 run() 方法,在 run() 方法中编写任务处理代码,然后创建这个 Thread 的子类。

    1. 直接创建 Thread 对象。

    class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
    

    Thread 的构造方法中,最重要的参数是 target,所以我们需要将一个 callable 对象赋值给它,线程才能正常运行。

    如果要让一个 Thread 对象启动,调用它的 start() 方法就好了。

    下面是代码示例。

    import threading
    import time
    
    def test():
    
        for i in range(5):
            print('test ',i)
            time.sleep(1)
    
    
    thread = threading.Thread(target=test)
    thread.start()
    
    for i in range(5):
        print('main ', i)
        time.sleep(1)
    

    上面代码很简单,在主线程上打印 5 次,在一个子线程上打印 5 次。

    运行结果如下:

    test  0
    main  0
    main  1
    test  1
    main  2
    test  2
    main  3
    test  3
    main  4
    test  4
    

    上面的 callable 没有参数,如果需要传递参数的话,args 是固定参数,kwargs 是可变参数。

    Thread 的名字

    每一个 Thread 都有一个 name 的属性,代表的就是线程的名字,这个可以在构造方法中赋值。

    如果在构造方法中没有个 name 赋值的话,默认就是 “Thread-N” 的形式,N 是数字。

    import threading
    import time
    
    def test():
    
        for i in range(5):
            print(threading.current_thread().name+' test ',i)
            time.sleep(1)
    
    
    thread = threading.Thread(target=test)
    thread.start()
    
    for i in range(5):
        print(threading.current_thread().name+' main ', i)
        time.sleep(1)
    

    通过 thread.current_thread() 方法可以返回线程本身,然后就可以访问它的 name 属性。

    上面代码运行结果如下:

    Thread-1 test  0
    MainThread main  0
    Thread-1 test  1
    MainThread main  1
    Thread-1 test  2
    MainThread main  2
    Thread-1 test  3
    MainThread main  3
    Thread-1 test  4
    MainThread main  4
    

    如果我们在 Thread 对象创建时,构造方法里面赋值。

    thread = threading.Thread(target=test,name='TestThread')
    

    那么,运行结果会变成这个样子。

    TestThread test  0
    MainThread main  0
    MainThread main  1
    TestThread test  1
    MainThread main  2
    TestThread test  2
    MainThread main  3
    TestThread test  3
    MainThread main  4
    TestThread test  4
    

    Thread 的生命周期

    1. 创建对象时,代表 Thread 内部被初始化。
    2. 调用 start() 方法后,thread 会开始运行。
    3. thread 代码正常运行结束或者是遇到异常,线程会终止。

    可以通过 Thread 的 is_alive() 方法查询线程是否还在运行。

    值得注意的是,is_alive() 返回 True 的情况是 Thread 对象被正常初始化,start() 方法被调用,然后线程的代码还在正常运行。

    import threading
    import time
    
    def test():
    
        for i in range(5):
            print(threading.current_thread().name+' test ',i)
            time.sleep(0.5)
    
    
    thread = threading.Thread(target=test,name='TestThread')
    # thread = threading.Thread(target=test)
    thread.start()
    
    for i in range(5):
        print(threading.current_thread().name+' main ', i)
        print(thread.name+' is alive ', thread.isAlive())
        time.sleep(1)
    

    在上面的代码中,我让 TestThread 比 MainThread 早一点结束,代码运行结果如下。

    TestThread test  0
    MainThread main  0
    TestThread is alive  True
    TestThread test  1
    MainThread main  1
    TestThread is alive  True
    TestThread test  2
    TestThread test  3
    MainThread main  2
    TestThread is alive  True
    TestThread test  4
    MainThread main  3
    TestThread is alive  False
    MainThread main  4
    TestThread is alive  False
    

    我们可以看到,主线程通过调用 TestThread 的 isAlive() 方法,准确查询到了它的存货状态。

    join() 提供线程阻塞手段。

    上面代码两个线程是同时运行的,但如果让一个先运行,一个后运行,怎么做呢?

    调用一个 Thread 的 join() 方法,可以阻塞自身所在的线程。

    import threading
    import time
    
    def test():
    
        for i in range(5):
            print(threading.current_thread().name+' test ',i)
            time.sleep(0.5)
    
    
    thread = threading.Thread(target=test,name='TestThread')
    thread.start()
    thread.join()
    
    for i in range(5):
        print(threading.current_thread().name+' main ', i)
        print(thread.name+' is alive ', thread.isAlive())
        time.sleep(1)
    

    主线程创建了 TestThread 对象后,让其 start,然后通过调用 join() 方法,实现等待。程序运行结果如下:

    TestThread test  0
    TestThread test  1
    TestThread test  2
    TestThread test  3
    TestThread test  4
    MainThread main  0
    TestThread is alive  False
    MainThread main  1
    TestThread is alive  False
    MainThread main  2
    TestThread is alive  False
    MainThread main  3
    TestThread is alive  False
    MainThread main  4
    TestThread is alive  False
    

    默认的情况是,join() 会一直等待对应线程的结束,但可以通过参数赋值,等待规定的时间就好了。

    def join(self, timeout=None):
    

    timeout 是一个浮点参数,单位是秒。

    如果我们更改上面的代码。

    thread.join(1.0)
    

    它的结果会是这样。

    TestThread test  0
    TestThread test  1
    MainThread main  0
    TestThread is alive  True
    TestThread test  2
    TestThread test  3
    MainThread main  1
    TestThread is alive  True
    TestThread test  4
    MainThread main  2
    TestThread is alive  False
    MainThread main  3
    TestThread is alive  False
    MainThread main  4
    TestThread is alive  False
    

    主线程只等待了 1 秒钟。

    Thread 中的 daemon 属性

    有同学可能会注意到,Thread 的构造方法中有一个 daemon 参数。默认是 None。

    那么,daemon 起什么作用呢?

    我们先看一段示例代码。

    import threading
    import time
    
    def test():
    
        for i in range(5):
            print(threading.current_thread().name+' test ',i)
            time.sleep(2)
    
    
    thread = threading.Thread(target=test,name='TestThread')
    # thread = threading.Thread(target=test,name='TestThread',daemon=True)
    thread.start()
    
    
    for i in range(5):
        print(threading.current_thread().name+' main ', i)
        print(thread.name+' is alive ', thread.isAlive())
        time.sleep(1)
    

    我们让主线程执行代码的时长比 TestThread 要短。

    程序运行结果如下。

    TestThread test  0
    MainThread main  0
    TestThread is alive  True
    MainThread main  1
    TestThread is alive  True
    TestThread test  1
    MainThread main  2
    TestThread is alive  True
    MainThread main  3
    TestThread is alive  True
    TestThread test  2
    MainThread main  4
    TestThread is alive  True
    TestThread test  3
    TestThread test  4
    

    MainThread 没有代码运行的时候,TestThread 还在运行。

    这是因为 MainThread 在等待其他线程的结束。

    TestThread 中 daemon 属性默认是 False,这使得 MainThread 需要等待它的结束,自身才结束。

    如果要达到,MainThread 结束,子线程也立马结束,怎么做呢?

    其实很简单,只需要在子线程调用 start() 方法之前设置 daemon 就好了。

    当然也可以在子线程的构造器中传递 daemon 的值为 True。

    thread = threading.Thread(target=test,name='TestThread',daemon=True)
    # thread.setDaemon(True)
    

    更改前面代码示例,运行结果如下

    TestThread test  0
    MainThread main  0
    TestThread is alive  True
    MainThread main  1
    TestThread is alive  True
    TestThread test  1
    MainThread main  2
    TestThread is alive  True
    MainThread main  3
    TestThread is alive  True
    TestThread test  2
    MainThread main  4
    TestThread is alive  True
    

    可以看到 MainThread 结束了 TestThread 也结束了。

    2.自定义类继承 Thread

    前面讲过,直接初始化一个 Thread,然后,现在还有一种方式就是自定义一个 Thread 的子类,然后复写它的 run() 方法。

    import threading
    import time
    
    
    class TestThread(threading.Thread):
    
        def __init__(self,name=None):
            threading.Thread.__init__(self,name=name)
    
        def run(self):
            for i in range(5):
                print(threading.current_thread().name + ' test ', i)
                time.sleep(1)
    
    
    thread = TestThread(name='TestThread')
    thread.start()
    
    
    for i in range(5):
        print(threading.current_thread().name+' main ', i)
        print(thread.name+' is alive ', thread.isAlive())
        time.sleep(1)
    

    上面的代码,我们自定义了 TestThread 这个类,然后继承了 threading.Thread。

    只有在 run() 方法中处理逻辑。最终代码运行结果如下:

    TestThread test  0
    MainThread main  0
    TestThread is alive  True
    TestThread test  1
    MainThread main  1
    TestThread is alive  True
    TestThread test  2
    MainThread main  2
    TestThread is alive  True
    MainThread main  3
    TestThread is alive  True
    TestThread test  3
    MainThread main  4
    TestThread test  4
    TestThread is alive  True
    

    这与之前的效果并无差异,但我还是推荐用这种方法,毕竟面向对象编程嘛。

    自此,Python 多线程编码技术就大致介绍完毕,大家可以进行实际代码编写了。

    但是,多线程编程的难点在于多个线程之间共享数据的同步,这是非常容易出错的地方,我将分别编写相应的博文去介绍一些高级的技术点。

    展开全文
  • Windows多线程编程

    2011-05-29 23:26:04
    Windows多线程编程Windows多线程编程Windows多线程编程Windows多线程编程Windows多线程编程Windows多线程编程Windows多线程编程
  • 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一次,其他的操作都是对这个传入的对象进行的,才能保证数据一致性,完整性和正确性。

    展开全文
  • 多线程编程指南 多线程编程指南 多线程编程指南 多线程编程指南 多线程编程指南
  • 网络socket编程实现并发服务器——多线程编程

    万次阅读 多人点赞 2019-04-05 00:20:19
    多线程编程

    一、多线程简介

    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测试工具连接并测试服务器的执行情况,我们可以发现服务器可以同时处理多个客户端的连接请求和通信,并在客户端断开时子线程退出,从而实现了服务器并发访问。

    展开全文
  • 两本书,大学学习哦,呵呵.windows环境下的多线程编程原理与应用 多线程编程实例教程
  • 深入浅出多线程编程实战(四)CAS与ABA问题

    万次阅读 多人点赞 2021-04-28 10:31:04
    深入浅出多线程编程实战(三)CAS与ABA问题 文章目录深入浅出多线程编程实战(三)CAS与ABA问题一、CAS二、ABA问题 一、CAS CAS全称:Compare-And-Swap(比较 and 替换),它是一条CPU并发原语 在日常开发中基本...
  • 线程与进程相比,它是一种花销小,...多编程并发在企业中开发显得尤为重要,本课程包含Windows多线程编程与C++11高并发编程,通过浅显易懂的代码与讲解,让你的多线程编程能力得到质的飞跃,具备开发高并发代码的能力!
  • Linux下多线程编程的概念、API接口使用、线程同步的掌握、线程池、协程等基本概念。
  • Java深入核心-多线程编程实战

    千人学习 2020-03-06 19:02:12
    对于一个 Java 程序员而言,能否熟练掌握多线程编程是判断他优秀与否的重要标准之一。因为并发编程是 Java 语言中最为晦涩的知识点,它涉及操作系统、内存、CPU、编程语言等多方面的基础能力,更为考验一个程序员的...
  • c 多线程编程01

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

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

    千次阅读 2019-06-21 17:40:29
    作为多任务实现的一种机制,多线程应用得非常广泛,相对于多进程,多线程不仅运行效率高,...一、多线程编程常用函数 1. int pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (*start_ro...
  • 多线程编程技术开发资料.pdf

    千次下载 热门讨论 2012-02-16 13:38:02
    多线程编程技术开发资料.pdf
  • C#多线程编程讲解 C#多线程编程讲解 C#多线程编程讲解 C#多线程编程讲解
  • C#多线程编程实战原书第二版 完整实例源码,非常好的C#多线程编程学习资源,需要VS2015。
  • Windows多线程编程技术与实例.pdf

    热门讨论 2014-05-26 11:56:58
     本书通过众多实例介绍了如何实现Windows下的多线程编程,既重点介绍了Win32API下的多线程编程和MFC下的多线程编程,又介绍了多线程机制在网络编程、图形编程和数据库中的应用。本书每一章都从简单的多线程实例出发...
  • 本例来自《java多线程编程调试模式》: 题意:模拟流水线上的工人,  工人一直在流水线上作业,零件(可看作客户端发的请求)一到达,工人就开始进行工作,无零件时工作处于等待状态 具体的业务代码有详注释: ...
  • c++多线程编程

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

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

    热门讨论 2015-05-31 12:26:21
    C#多线程编程实战Code源代码 资源是从华章出版社官网下载的
  • Java多线程编程 深入详解

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

    万次阅读 多人点赞 2018-08-28 18:44:09
    多线程在编程中有相当重要的地位,我们在实际开发时或者找工作面试时总能遇到多线程的问题,对多线程的理解程度从一个侧面...),但Windows系统为我们提供了相关API,我们可以使用他们来进行多线程编程。 创建线...
  • C#多线程编程实例 线程与窗体交互源码,该示例演示如何在线程安全的模式下调用Windows窗体上的控件。
  • 1.章节结构图 ...多线程编程一方面有助于提高系统吞吐量、提高系统的响应性、充分利用多核处理器、最小化对系统资源的使用和简化程序的结构;另一方面面临线程安全、线程活性、上下文切换和可靠性等问题 ...
  • 作为开发者,我们除了能够熟练使用常用的多线程编程技术外,对于复杂场景的多线程及决方案也要有所了解,例如互相依赖的任务的线程分配,多队列组的应用、死锁场景的分析和优化等。 本课程将详细介绍pthread、...
  • QT中的多线程编程

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

    2011-02-11 12:16:23
    linux多线程编程,需要从事linux多线程编程可以看看。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 685,405
精华内容 274,162
关键字:

多线程编程