【第七节】python多线程及网络编程

news2024/11/23 10:45:26

目录

一、python多线程

1.1 多线程的作用

1.2 python中的 threading 模块

1.3 线程锁

二、python网络编程

2.1 通过socket访问网络

2.2 python2.x中的编码问题

2.3 python3的编码问题


一、python多线程

1.1 多线程的作用

多线程技术在计算机编程中扮演着重要的角色,它主要有以下几个作用:

        1. **提高程序的响应性**:在单线程程序中,如果某个操作需要较长时间,比如读取大文件或进行复杂计算,整个程序会在这段时间内无法响应用户的其他操作。而多线程允许程序在执行耗时操作的同时,其他线程可以继续响应用户输入,从而提高程序的响应速度和用户体验。

        2. **提高资源利用率**:现代计算机通常有多个处理器或核心。多线程允许程序同时运行在多个处理器上,从而更充分地利用系统资源,提高处理速度和效率。

        3. **简化程序结构**:通过将复杂的任务分解为多个并行的线程,可以使程序的逻辑更加清晰和模块化。每个线程负责一部分任务,便于管理和维护。

        4. **实现并发操作**:在某些应用场景中,需要同时进行多个独立的任务,如服务器处理多个客户端请求、图形界面同时响应用户输入和后台数据处理等。多线程使得这些并发操作成为可能。

        5. **提高执行效率**:对于可以并行执行的任务,多线程可以显著减少总的执行时间。例如,在数据处理、图像渲染、科学计算等领域,多线程可以大幅提升处理速度。

        然而,多线程编程也带来了一些挑战,如线程同步问题、死锁风险、资源竞争等,需要开发者仔细设计和实现,以确保程序的正确性和稳定性。

1.2 python中的 threading 模块

        Python中用于多线程编程的内置模块是 `threading`。下面我将详细解释如何使用 `threading` 模块来启动多线程。

### 使用 `threading` 模块启动多线程

1. **导入 `threading` 模块**:

import threading

2. **定义线程函数**:
   这个函数将作为新线程的入口点。

   def my_thread_function(arg1, arg2):
       # 线程执行的代码
       print(f"Thread is running with arguments: {arg1}, {arg2}")

3. **创建 `Thread` 对象**:
   在创建 `Thread` 对象时,可以传入线程函数和参数。

thread = threading.Thread(target=my_thread_function, args=("hello", "world"))

4. **启动线程**:
   调用 `start()` 方法启动线程。

thread.start()

5. **等待线程完成(可选)**:
   如果需要等待线程执行完毕,可以调用 `join()` 方法。

thread.join()

### 完整示例

以下是一个完整的示例,展示了如何使用 `threading` 模块创建和启动多个线程:

import threading

def my_thread_function(arg1, arg2):
    print(f"Thread {threading.current_thread().name} is running with arguments: {arg1}, {arg2}")

# 创建多个线程
threads = []
for i in range(5):
    thread = threading.Thread(target=my_thread_function, args=(f"hello_{i}", f"world_{i}"))
    threads.append(thread)
    thread.start()

# 等待所有线程完成
for thread in threads:
    thread.join()

print("All threads have finished.")

        在这个示例中,我们创建了5个线程,每个线程执行 `my_thread_function` 函数,并传递不同的参数。最后,我们使用 `join()` 方法确保主线程等待所有子线程完成后再继续执行。

        通过这种方式,您可以利用 `threading` 模块在Python中实现多线程编程,从而提高程序的并发性和响应性。

1.3 线程锁

        在多线程编程中,所有线程共享代码和数据资源。这种共享性带来了一个主要风险:多个线程可能同时访问和修改同一个变量,导致不可预期的结果。为了解决这一问题,大多数编程语言提供了锁机制来确保线程安全。

### 问题代码示例

import threading

g_Num = 0

def threadProc():
    global g_Num
    for i in range(1000000):
        g_Num = g_Num + 1  # 修改数据

thread1 = threading.Thread(name="hello1", target=threadProc)
thread2 = threading.Thread(name="hello2", target=threadProc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()

print(g_Num)

        在这个示例中,两个线程同时对 `g_Num` 进行递增操作,导致最终打印出来的数字不可预期。

### 使用锁机制解决问题

        为了确保线程安全,可以使用锁来保护对 `g_Num` 的访问。以下是修正后的代码:
 

import threading

lock = threading.Lock()
g_Num = 0

def threadProc():
    global g_Num
    for i in range(1000000):
        lock.acquire()  # 获取锁
        g_Num = g_Num + 1  # 修改数据
        lock.release()  # 释放锁

thread1 = threading.Thread(name="hello1", target=threadProc)
thread2 = threading.Thread(name="hello2", target=threadProc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()

print(g_Num)

        通过使用锁,我们确保在任何时刻只有一个线程能够进入锁定范围并修改 `g_Num`,从而避免了竞态条件,确保最终结果的可预期性。

二、python网络编程

2.1 通过socket访问网络

Python 提供了两种不同层次的网络服务接口:

        1. **低级网络服务**:这一层支持基本的 Socket 功能,它实现了标准的 BSD Sockets API,允许开发者访问底层操作系统 Socket 接口的所有方法,从而进行更底层的网络操作。

        2. **高级网络服务**:这一层包含模块 SocketServer,它提供了一系列服务器中心类,旨在简化网络服务器的开发过程,使得开发者能够更快速地构建网络应用。

**什么是 Socket?**

        Socket,又称为“套接字”,是应用程序用于网络通信的一种抽象。通过 Socket,应用程序可以发送请求或响应网络请求,实现不同主机间或同一台计算机上不同进程间的通信。Socket 是网络编程的基础,它封装了复杂的网络通信细节,使得开发者能够更容易地编写网络应用程序。

socket()函数

Python 中,我们用 socket()函数来创建套接字,语法格式如下:

socket.socket([family[, type[, proto]]])

参数

  • family: 套接字家族可以使 AF_UNIX 或者 AF_INET。
  • type: 套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREAMSOCK_DGRAM
  • protocol: 一般不填默认为 0。

Socket 对象(内建)方法

简单示例如下:

服务端代码:

# 导入 socket 模块
import socket

def main():
    print("~~~~~服务端启动~~~~~")

    # 1. 创建 socket
    sSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 绑定
    sSock.bind(('192.168.1.125', 1234))  # IP地址 可用本地IP测试: 127.0.0.1

    # 3. 监听
    sSock.listen(5)

    # 4. 处理连接
    cSock, addr = sSock.accept()
    print("客户端连接成功")
    cSock.send(str('欢迎:').encode('utf-8'))

    # 5、6 发送、接收数据
    while True:
        print(cSock.recv(1024).decode('utf-8'))
        inStr = input('>>>: ')
        if inStr == 'quit':
            break
        cSock.send(str(inStr).encode('utf-8'))

    # 7. 关闭套接字
    cSock.close()
    sSock.close()

if __name__ == "__main__":
    main()

客户端代码:

# 导入 socket 模块
import socket

def main():
    print("~~~~~客户端启动~~~~~")

    # 1. 创建 socket
    cSock = socket.socket()

    # 2. 连接服务器
    host = '192.168.1.125'  # IP地址 可用本地IP测试: 127.0.0.1
    port = 1234  # 设置端口号
    cSock.connect((host, port))

    # 3、4 发送、接收数据
    while True:
        print(cSock.recv(1024).decode('utf-8'))
        inStr = input('>: ')
        if inStr == 'quit':
            break
        cSock.send(str(inStr).encode('utf-8'))

    # 5. 关闭套接字
    cSock.close()

if __name__ == "__main__":
    main()

2.2 python2.x中的编码问题

        在Python 2.x版本中,存在两个主要的字符串类:`unicode` 和 `str`,它们都继承自 `basestring`。

        `str` 类是带编码的,默认情况下使用 ASCII 编码。因此,如果你的程序中包含中文字符串,默认情况下会报错。可以通过设置Python 2中的字符默认编码来解决这个问题。

  # coding:utf-8   # 默认使用UTF-8编码
  # coding:gbk     # 默认使用GBK编码

        `unicode` 类是不带编码的,用于表示已知文明中的任何一个字符。需要注意的是,`unicode` 并不是一种编码方式。
  示例:

  ul = u"中国"  # 字符串 unicode类型
  print ul      # 输出: u'\u4e2d\u56fd'
  print len(ul) # 输出: 2

  u2 = u'hello'
  print u2      # 输出: u'hello'
  print len(u2) # 输出: 5

  sl = "中国"   # str 类型,字节串
  print sl      # 输出: '\xd6\xd0\xb9\xfa'(中文 GBK 编码,控制台不指定中文默认 GBK)
  print len(sl) # 输出: 4

  s2 = "hello"
  print s2      # 输出: 'hello'
  print len(s2) # 输出: 5

在Python 2中,可以在字符串和字节串之间进行转换:
- `encode`:将字符串按指定方式进行编码,转换成字节流(`str`),存放在内存中。
- `decode`:将字节流按指定方式进行解码,转换成字符串(`unicode`),用于显示。

可以使用 `chardet` 模块来判断字节流的编码:

import chardet
raw = u'12AB好'
print chardet.detect(raw.encode('utf-8'))  # 输出: {'encoding': 'utf-8', 'confidence': 0.99}
print chardet.detect(raw.encode('gbk'))    # 输出: {'encoding': 'GB2312', 'confidence': 0.99}

        在C++中,多字节集通常是GBK编码,而宽字节集是UTF-16编码。字符使用哪种方式进行编码,就应该使用哪种方式进行解码。以下是几种常见情况:
1. C++端发送过来的是GBK编码,我们需要显示,那么应该使用 `decode("GBK")` 转换成 `unicode` 便于显示。
2. C++端发送过来的是UTF-16编码,我们需要显示,那么应该使用 `decode("UTF-16")` 转换成 `unicode` 便于显示。
3. C++端发送过来的数据需要Python端转发到其他C++端,不需要转换。
4. Python端要直接给C++端发送字符串,那么应该根据C++端使用多字节集还是宽字节集,使用 `encode("GBK")` 或者 `encode("UTF-16")` 之后再发送给C++端。
5. 如果你直接使用字节串,那么应该先使用 `decode("UTF-8")` 转换成 `unicode`,再使用 `encode("GBK")` 或者 `encode("UTF-16")` 再发送给C++端。

示例代码:

c++端代码:

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

// 数据包 1024 + 4 + 4 = 1032
struct NETMSGINFO {
    int MSGTYPE;       // 消息类型
    int nMsgLen;       // 消息大小
    char szMsgBuff[1024]; // 消息内容
};

int main() {
    // 1. 初始化环境
    WSADATA wsd = {};
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
        std::cerr << "WSAStartup failed!" << std::endl;
        return 1;
    }

    // 2. 创建套接字
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET) {
        std::cerr << "socket creation failed!" << std::endl;
        WSACleanup();
        return 1;
    }

    // 3. 连接
    sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(0x1234);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
        std::cerr << "connect failed!" << std::endl;
        closesocket(sock);
        WSACleanup();
        return 1;
    }

    // 4. 发送数据
    NETMSGINFO netbuf = {};
    netbuf.MSGTYPE = 1; // 可以用宏代替,上线
    strcpy_s(netbuf.szMsgBuff, 1024, "xxx:上线了");
    netbuf.nMsgLen = strlen(netbuf.szMsgBuff); // 字符长度,注意Python中字符不以0结尾
    if (send(sock, (char*)&netbuf, sizeof(netbuf), 0) == SOCKET_ERROR) {
        std::cerr << "send failed!" << std::endl;
        closesocket(sock);
        WSACleanup();
        return 1;
    }

    // 5. 接收数据
    if (recv(sock, (char*)&netbuf, sizeof(netbuf), 0) == SOCKET_ERROR) {
        std::cerr << "recv failed!" << std::endl;
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    std::cout << netbuf.szMsgBuff << std::endl;

    // 6. 清理环境
    closesocket(sock);
    WSACleanup();
    return 0;
}

python端代码:

import socket
import struct

def main():
    # 创建TCP套接字
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 绑定端口
    sock.bind(("127.0.0.1", 0x1234))
    
    # 监听
    sock.listen(socket.SOMAXCONN)
    
    # 等待连接
    clientsock, addr = sock.accept()
    print(f"Connection from {addr}")
    
    # 获取消息大小是发送数据结构大小
    Msg = clientsock.recv(1032)
    
    # 进行格式拆包,由于字符长度不确定,暂时不解包消息
    msgtype, nMsgLen = struct.unpack('ii', Msg[0:8])
    
    # 第二次根据长度解包,指定解包字符长度,使用切片方式
    msgbuff, = struct.unpack(f'{nMsgLen}s', Msg[8:8+nMsgLen])
    
    # 字符需要进行解码,因为VS中默认是以GBK编码方式
    print(msgbuff.decode('gbk'))
    
    # 回复客户端消息,构建一个数据包
    # 这个数据需要进行GBK编码,否则VS中解析不了字符
    sendbug = '你好\n'.encode('gbk')
    
    # 打包数据
    msg = struct.pack('ii1024s', 0, len(sendbug), sendbug)
    
    # 发送数据
    clientsock.send(msg)
    
    # 关闭套接字
    clientsock.close()
    sock.close()

if __name__ == "__main__":
    main()

2.3 python3的编码问题

        在Python 3中,默认使用UTF-8编码,并且明确区分了文本字符和二进制数据,分别用`str`和`bytes`类型表示。

s1 = "abc"  # str类型,字符串
s2 = b"abc"  # bytes类型,二进制字节流
s1 = "中国"  # str类型,字符串
s2 = b"中国"  # bytes类型,不支持非ASCII字符,这样会报错

        在Python 2中,`str`类型在Python 3中对应`bytes`类型,表现为字节,转换是通过`encode`方法,用于存储。

        在Python 2中,`Unicode`类型在Python 3中对应`str`类型,表现为字符,转换是通过`decode`方法,用于显示。

`encode`和`decode`方法用于在`str`和`bytes`之间进行转换。

示例:

s = "18CM好棒"
print(s.encode())  # 默认使用UTF-8编码
# 输出: b'18CM\xe5\xa5\xbd\xe6\xa3\x92'
print(s.encode("gbk"))  # 使用GBK编码
# 输出: b'18CM\xba\xc3\xb0\xb2'

print(b'18CM\xe5\xa5\xbd\xe6\xa3\x92'.decode())  # 默认使用UTF-8解码
# 输出: '18CM好棒'
print(b'18CM\xba\xc3\xb0\xb2'.decode("gbk"))  # 使用GBK解码
# 输出: '18CM好棒'

需要注意的是,`encode`和`decode`方法的默认参数都是UTF-8。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1986801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

五大避坑要点,让你轻松避开99%的雷区!随身wifi京东口碑排行榜,随身wifi推荐第一名!

在数字浪潮中&#xff0c;随身WiFi成为我们不可或缺的伴侣&#xff0c;但市场纷繁复杂&#xff0c;如何挑选成为难题。以下五大避坑要点&#xff0c;让你轻松避开99%的雷区&#xff01; 1.避小就大&#xff0c;信赖旗舰店&#xff1a;远离无名小品牌&#xff0c;选择知名品牌的…

Javacript 高级程序设计(系统学习)

以下为阅读 《Javacript 高级程序设计》部分笔记&#xff0c;待继续完善&#xff0c;后续会进行章节拆分。 第1章 什么是 javascript 历史回顾js 实现 / es / dom / bomjs 版本 javascript 最初为什么设计为单线程&#xff1f; JavaScript 最初设计为单线程的主要原因是出于简…

体验 Whisper ,本地离线部署自己的 ASR 语音识别服务

需求背景 最近看视频&#xff0c;过几天后经常忘记内容&#xff0c;所以有了把重点内容总结提炼到自己知识库的需求&#xff0c;这涉及到了提取视频中的音频数据、离线语音识别等功能。 提取视频中的音频数据&#xff0c;可以使用格式工厂或 FFmpeg 等工具&#xff0c; FFmpe…

详细解析socket

我最近开了几个专栏&#xff0c;诚信互三&#xff01; > |||《算法专栏》&#xff1a;&#xff1a;刷题教程来自网站《代码随想录》。||| > |||《C专栏》&#xff1a;&#xff1a;记录我学习C的经历&#xff0c;看完你一定会有收获。||| > |||《Linux专栏》&#xff1…

书生大模型实战营第三期——入门岛——Git基础知识

第三关&#xff1a;Git基础知识 任务如下&#xff1a; 任务描述 破冰活动&#xff1a;自我介绍 每位参与者提交一份自我介绍。 提交地址&#xff1a;GitHub - InternLM/Tutorial: LLM&VLM Tutorial 的 camp3 分支&#xff5e;实践项目&#xff1a;构建个人项目 创建一个个人…

PDF发票解析并将信息回填到前端(2)前端页面

本人前端基础薄弱&#xff0c;此处的前端仅仅是一个练习展示 1. 创建一个前端项目 打开终端使用以下命令创建一个基于webpack模板的新项目 vue init webpack my-project输入命令之后一直点击enter知道项目创建完成即可 进入项目&#xff0c;安装并运行 $ cd my-project //…

ExtJS生成日历组件

文章目录 1.添加日历组件代码2.引入日历组件,创建了补签和取消签到,以及翻页显示的事件 1.添加日历组件代码 首先找到自己项目中对应的Ext的目录,并将日历组件添加到calendar这个目录下,我的是KwDatePicker.js 日历组件代码如下: Ext.define(Ext.calendar.KwDatePicker, {ex…

python six模块是什么

six模块是Python2和3兼容性库&#xff0c;它是为了解决Python2和Python3代码兼容性而产生的&#xff0c;众所周知Python2和Python3版本的分裂给Python开发者们带来了很大的烦恼&#xff0c;为了使代码同时兼容两个版本&#xff0c;往往要增加大量的代码&#xff0c;典型的就有u…

著名人工智能新经济数字经济新能源新质生产力讲师培训师教授专家唐兴通分享人工智能社会学商业模式创新人工智能就业工作与教育学习出海跨境数字化转型数字营销数字销售

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 2024 年是人工智能在工作中真正应用的一年。根据微软和领英进行的调查&#xff08;2024年5月&#xff09;&#xff0c;在过去六个月中&#xff0c;生成式人工智能的使用量几乎翻了一番&#xff0c;全球75%的…

领夹麦克风哪个品牌音质最好?八月领夹式麦克风排行榜

随着移动互联技术的飞速发展&#xff0c;视频内容的消费与创作正以前所未有的速度增长。从直播带货的兴起&#xff0c;到短视频平台的火爆&#xff0c;音频质量作为内容体验的重要组成部分&#xff0c;越来越受到创作者的重视。在这一背景下&#xff0c;无线领夹麦克风凭借其小…

6-8 残差网络(ResNet)

随着我们设计越来越深的网络&#xff0c;深刻理解“新添加的层如何提升神经网络的性能”变得至关重要。更重要的是设计网络的能力&#xff0c;在这种网络中&#xff0c;添加层会使网络更具表现力&#xff0c; 为了取得质的突破&#xff0c;我们需要一些数学基础知识。 残差网络…

Web端高效元件库——高端元件,匠心设计

原型设计&#xff0c;不仅是产品从构想到落地不可或缺的桥梁&#xff0c;更是深化用户体验优化策略的核心环节。Axure&#xff0c;作为原型设计领域的领航者&#xff0c;凭借其卓越的交互设计引擎与无与伦比的灵活性&#xff0c;赢得了产品设计师们的广泛赞誉&#xff0c;成为他…

NiFi :1 初识这把“十年一剑”的利器

--->更多内容&#xff0c;请移步“鲁班秘笈”&#xff01;&#xff01;<--- “现在AI和数据处理密不可分&#xff0c;80%的企业可以利用Apache NiFi轻松解决复杂的数据问题&#xff0c;快速完成场景建设。犹如花上百来块钱在家享受一顿不亚于五星级西餐厅的法式大餐。对…

非负数、0和正数 限制最大值且保留两位小数在elementpuls表单中正则验证

一、结构 <el-form-item label="单价:" prop="price"><el-inputv-model.trim="formData.price"placeholder="请输入"@blur="formMethod.fixTwo"><template #append>(元)</template></el-input…

电源芯片测试系统NSAT2000对比传统ATE测试软件有哪些优势?

随着近几年电源芯片的研究和发展&#xff0c;电源芯片向着高度的集成化、智能化的趋势发展&#xff0c;电源芯片想不过去有了更全面的功能&#xff0c;更稳定可靠的优势。相应的市场中电源芯片的测试系统同样也百花齐放&#xff0c;各类ATE测试软件层出不穷。其中纳米软件的NSA…

系统出现高CPU可能风险因素整理

文章目录 死循环无限递归序列化加解密正则表达式计算密集型任务大流量Full GC资源竞争/死锁I/O阻塞外部接口调用 死循环 死循环是最常见的原因之一。当代码中存在无穷循环&#xff08;例如在多线程环境下的HashMap线程不安全问题或分页查询条件不明确导致的无限循环&#xff0…

C++分析红黑树

目录 红黑树介绍 红黑树的性质与平衡控制关系 红黑树节点的插入 情况1&#xff1a;不需要调整 情况2&#xff1a;uncle节点为红色 情况3&#xff1a;uncle节点为黑色 总结与代码实现 红黑树的删除&#xff08;待实现&#xff09; 红黑树的效率 红黑树介绍 红黑树是第二种平衡二…

e6.利用 docker 快速部署自动化运维平台

利用 docker 快速部署自动化运维平台 1. 安装docker2. 拉取镜像3. 启动容器4. 初始化5. 访问测试 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主 机在线终端、文件在线上传下载、应用发布部署、在线任务计划、配置中…

基于tcp,html,数据库的在线信息查询系统项目总结

1.项目背景 在线信息查询系统是一种可用于检索和展示各种信息的计算机程序或平台。主要特点包括&#xff1a; 用户接口&#xff1a;通常提供友好的界面&#xff0c;用户可以方便地输入查询条件。 数据存储&#xff1a;系统往往连接到数据库&#xff0c;存储大量信息&#xf…

几个g视频能压缩成几百mb吗?分享5种视频压缩方法

现如今&#xff0c;高清视频已成为我们日常生活和工作中的重要组成部分。然而&#xff0c;随着视频分辨率和时长的增加&#xff0c;文件体积也随之膨胀&#xff0c;给存储和传输带来了巨大挑战。这时候就需要给视频进行压缩处理&#xff0c;下面给大家分享5种视频压缩方法&…