zoukankan      html  css  js  c++  java
  • 系统架构——多线程的应用

    什么是多线程,这在相关计算机原理的书籍里都有介绍,通常所说的多线程是指进程内的多线程,由进程创建一个私有线程表,自行管理自己的线程,这样好处是线程阻塞了,只会挂起进程,而不会影响到整个操作系统的运行。每个线程都有自己的栈,每创建一个线程就会分配一定的资源给线程,这就是为什么说要谨慎使用线程,否则会造成不必要的资源浪费,如果在Windows上,默认线程栈只有1M,不小心还会造成堆栈溢出。

    C#、Java的多线程,最终映射到操作系统的本地线程实现。

    我们可以通过代码演示一下,单线程和多线程在内存使用上的区别:

    #include <iostream>
    #include <time.h>
    #include <math.h>
    #include <thread>
    #include <chrono>
    using namespace std::chrono_literals;
    void printSomething(int index) {
    
    	int a = index;
    	
    	//去掉以下注释,以多线程的方式会报栈溢出
    	//char str[1024*996];
    
    	printf("%xd->%d
    ", &a, a);
    	std::this_thread::sleep_for(1s);
    }
    int main()
    {
    	std::cout << "non-thread
    ";
    	
    	
    	for (int i = 0; i < 5; i++) {
    		//单线程输出相同的变量地址
    		printSomething(i);
    	}
    	std::cout << "thread
    ";
    	std::thread t1[5];
    	for (int i = 0; i < 5; i++) {
    		//多线程输出不同的变量地址
    		t1[i]=std::thread(printSomething, i);
    	}
    
    	int x;
    	scanf_s("%d", &x);
    }
    

    输出结果:

    non-thread
    c1aff754d->0
    c1aff754d->1
    c1aff754d->2
    c1aff754d->3
    c1aff754d->4
    thread
    c1bff3a4d->0
    c21ff504d->4
    c1eff524d->1
    c1fff8f4d->2
    c20ff3e4d->3
    

    所以对于线程,是不能烂用的,比如一个Socket服务器,通常会用单独线程去处理客户端的连接,如果每个连接都会开一个新的线程,势必对服务器的性能造成影响,因为要不停地分配栈空间,会浪费CPU时间和内存空间。特别是长连接的情况下,如果缓存中一直没有数据,那关联的线程就一直挂在那边,不做任何事,白浪费的资源。这种情况下,就会用到线程的另一种机制——线程池。可以让一个线程遍历所有的Socket,装有数据的扔给一个空闲的线程去处理就行了。

    我们可以预先设置一组线程池,Windows上提供了一组Api用于实现线程池,原理就是将创建好的线程(绑定一个空函数)放在那等待,如果有任务来了,则发送一个解锁信号,领到任务的执行任务,没领到的继续等待。下边以Windows Api举个例子。

    #include <iostream>
    #include <time.h>
    #include <math.h>
    #include <thread>
    #include <chrono>
    #include <vector>
    #include <queue>
    #include <Windows.h>
    
    using namespace std::chrono_literals;
    
    const int len = 30;
    HANDLE handle[len];//事件通知
    int data[len];//测试数据
    //任务
    void printSomething(int index) {
    
    	int a = index;
    	auto tid = std::this_thread::get_id();
    	printf("%d:%xd->%d
    ", tid, &a, a);
    	std::this_thread::sleep_for(200ms);
    }
    //任务回调
    VOID CALLBACK workCall(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work) {
    	int* index = (int*)Context;
    	printSomething(*index);
    	SetEvent(handle[*index]);
    }
    
    int main()
    {
    	std::cout << "non-thread
    ";
    	for (int i = 0; i < len; i++) {
    		//单线程输出相同的变量地址
    		printSomething(i);
    	}
    	std::cout << "thread
    ";
    	TP_CALLBACK_ENVIRON CallBackEnviron;
    	InitializeThreadpoolEnvironment(&CallBackEnviron);
    	auto pool = CreateThreadpool(NULL);
    	SetThreadpoolThreadMinimum(pool, 2);
    	SetThreadpoolThreadMaximum(pool, 2);
    
    
    	for (int i = 0; i < len; i++) {
    		data[i] = i;
    		handle[i] = CreateEvent(NULL, false, false, NULL);
    		auto work = CreateThreadpoolWork(workCall, &data[i], &CallBackEnviron);
    		//多线程输出不同的变量地址
    		SubmitThreadpoolWork(work);
    	}
    
        //等待所有线程结束
    	WaitForMultipleObjects(5, handle, true, INFINITE);
    	CloseThreadpool(pool);
    	char x=getchar();
    }
    
    

    输出结果:

    non-thread
    57320:bf56fa84d->0
    57320:bf56fa84d->1
    57320:bf56fa84d->2
    57320:bf56fa84d->3
    57320:bf56fa84d->4
    57320:bf56fa84d->5
    57320:bf56fa84d->6
    57320:bf56fa84d->7
    57320:bf56fa84d->8
    57320:bf56fa84d->9
    57320:bf56fa84d->10
    57320:bf56fa84d->11
    57320:bf56fa84d->12
    57320:bf56fa84d->13
    57320:bf56fa84d->14
    57320:bf56fa84d->15
    57320:bf56fa84d->16
    57320:bf56fa84d->17
    57320:bf56fa84d->18
    57320:bf56fa84d->19
    57320:bf56fa84d->20
    57320:bf56fa84d->21
    57320:bf56fa84d->22
    57320:bf56fa84d->23
    57320:bf56fa84d->24
    57320:bf56fa84d->25
    57320:bf56fa84d->26
    57320:bf56fa84d->27
    57320:bf56fa84d->28
    57320:bf56fa84d->29
    thread
    51292:bfcff574d->0
    22580:bfdff834d->1
    29532:bfeff3d4d->2
    81420:bffff184d->3
    80700:c00ff684d->4
    51292:bfcff574d->5
    22580:bfdff834d->6
    29532:bfeff3d4d->7
    81420:bffff184d->8
    77684:c01ff3d4d->9
    80700:c00ff684d->10
    51292:bfcff574d->11
    22580:bfdff834d->12
    29532:bfeff3d4d->13
    81420:bffff184d->14
    54804:bf8ff644d->15
    77684:c01ff3d4d->16
    80700:c00ff684d->17
    51292:bfcff574d->18
    22580:bfdff834d->19
    29532:bfeff3d4d->20
    81420:bffff184d->21
    56548:bfbff154d->22
    54804:bf8ff644d->23
    77684:c01ff3d4d->24
    80700:c00ff684d->25
    57176:c02ff264d->26
    51292:bfcff574d->27
    22580:bfdff834d->28
    29532:bfeff3d4d->29

    从输出结果中的线程id,可以看出线程被重复使用了,栈空间的指针也是相同的。

    附C#示例:

    using System;
    using System.Threading;
    
    public class Example 
    {
        public static void Main() 
        {
            // Queue the task.
            ThreadPool.QueueUserWorkItem(ThreadProc);
            Console.WriteLine("Main thread does some work, then sleeps.");
            Thread.Sleep(1000);
    
            Console.WriteLine("Main thread exits.");
        }
    
        // This thread procedure performs the task.
        static void ThreadProc(Object stateInfo) 
        {
            // No state object was passed to QueueUserWorkItem, so stateInfo is null.
            Console.WriteLine("Hello from the thread pool.");
        }
    }
    // The example displays output like the following:
    //       Main thread does some work, then sleeps.
    //       Hello from the thread pool.
    //       Main thread exits.
    
  • 相关阅读:
    Spring学习笔记02-配置bean(第七周)
    Spring学习笔记01
    构建之法阅读笔记02
    keep running-团队视频分析初步总结
    影视分享App(三)(第六周)
    Delphi 正则表达式语法(6): 贪婪匹配与非贪婪匹配
    Delphi 正则表达式语法(5): 边界
    Delphi 正则表达式语法(4): 常用转义字符与 .
    Delphi 正则表达式语法(3): 匹配范围
    Delphi 正则表达式语法(2): 或者与重复
  • 原文地址:https://www.cnblogs.com/icoolno1/p/12814142.html
Copyright © 2011-2022 走看看