328 lines
16 KiB
Python
328 lines
16 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
|
|||
|
|
from docx import Document
|
|||
|
|
from docx.shared import Pt, Inches, Cm, Twips
|
|||
|
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|||
|
|
from docx.enum.table import WD_TABLE_ALIGNMENT
|
|||
|
|
from docx.oxml.ns import qn
|
|||
|
|
from docx.oxml import OxmlElement
|
|||
|
|
|
|||
|
|
def set_cell_border(cell, **kwargs):
|
|||
|
|
"""Set cell borders"""
|
|||
|
|
tc = cell._tc
|
|||
|
|
tcPr = tc.get_or_add_tcPr()
|
|||
|
|
tcBorders = OxmlElement('w:tcBorders')
|
|||
|
|
for edge in ('top', 'left', 'bottom', 'right'):
|
|||
|
|
if edge in kwargs:
|
|||
|
|
element = OxmlElement(f'w:{edge}')
|
|||
|
|
element.set(qn('w:val'), kwargs[edge].get('val', 'single'))
|
|||
|
|
element.set(qn('w:sz'), str(kwargs[edge].get('sz', 4)))
|
|||
|
|
element.set(qn('w:color'), kwargs[edge].get('color', '000000'))
|
|||
|
|
tcBorders.append(element)
|
|||
|
|
tcPr.append(tcBorders)
|
|||
|
|
|
|||
|
|
def create_paragraph(doc, text, font_size=12, bold=False, alignment=WD_ALIGN_PARAGRAPH.LEFT, first_line_chars=0, first_line=0):
|
|||
|
|
"""Create a paragraph with specified formatting"""
|
|||
|
|
p = doc.add_paragraph()
|
|||
|
|
p.alignment = alignment
|
|||
|
|
# Set paragraph spacing
|
|||
|
|
p.paragraph_format.line_spacing = Pt(18)
|
|||
|
|
if first_line_chars > 0:
|
|||
|
|
p.paragraph_format.first_line_chars = first_line_chars
|
|||
|
|
if first_line > 0:
|
|||
|
|
p.paragraph_format.first_line_indent = Pt(first_line)
|
|||
|
|
|
|||
|
|
run = p.add_run(text)
|
|||
|
|
run.font.size = Pt(font_size)
|
|||
|
|
run.font.bold = bold
|
|||
|
|
run.font.name = '宋体'
|
|||
|
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
|||
|
|
return p
|
|||
|
|
|
|||
|
|
def create_heading(doc, text, level=1):
|
|||
|
|
"""Create a section heading"""
|
|||
|
|
p = doc.add_paragraph()
|
|||
|
|
p.paragraph_format.line_spacing = Pt(18)
|
|||
|
|
p.paragraph_format.space_before = Pt(12 if level == 1 else 6)
|
|||
|
|
p.paragraph_format.space_after = Pt(6 if level == 1 else 3)
|
|||
|
|
|
|||
|
|
run = p.add_run(text)
|
|||
|
|
run.font.size = Pt(14 if level == 1 else 12)
|
|||
|
|
run.font.bold = True
|
|||
|
|
run.font.name = '宋体'
|
|||
|
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
|||
|
|
return p
|
|||
|
|
|
|||
|
|
def create_code_paragraph(doc, text):
|
|||
|
|
"""Create a paragraph with monospace font for code"""
|
|||
|
|
p = doc.add_paragraph()
|
|||
|
|
p.paragraph_format.line_spacing = Pt(14)
|
|||
|
|
p.paragraph_format.first_line_indent = Pt(0)
|
|||
|
|
|
|||
|
|
run = p.add_run(text)
|
|||
|
|
run.font.size = Pt(10)
|
|||
|
|
run.font.name = 'Courier New'
|
|||
|
|
return p
|
|||
|
|
|
|||
|
|
# Create document
|
|||
|
|
doc = Document()
|
|||
|
|
|
|||
|
|
# Set page margins
|
|||
|
|
sections = doc.sections
|
|||
|
|
for section in sections:
|
|||
|
|
section.top_margin = Inches(0.8)
|
|||
|
|
section.bottom_margin = Inches(0.8)
|
|||
|
|
section.left_margin = Inches(0.4)
|
|||
|
|
section.right_margin = Inches(0.4)
|
|||
|
|
|
|||
|
|
# Title
|
|||
|
|
p = doc.add_paragraph()
|
|||
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|||
|
|
run = p.add_run("网络空间安全学院")
|
|||
|
|
run.font.size = Pt(16)
|
|||
|
|
run.font.bold = True
|
|||
|
|
run.font.name = '华文中宋'
|
|||
|
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '华文中宋')
|
|||
|
|
|
|||
|
|
p = doc.add_paragraph()
|
|||
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|||
|
|
run = p.add_run("实验报告(电子版)")
|
|||
|
|
run.font.size = Pt(16)
|
|||
|
|
run.font.bold = True
|
|||
|
|
run.font.name = '华文中宋'
|
|||
|
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '华文中宋')
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Info table
|
|||
|
|
table = doc.add_table(rows=3, cols=6)
|
|||
|
|
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
|||
|
|
|
|||
|
|
# Row 1: Experiment name
|
|||
|
|
row = table.rows[0]
|
|||
|
|
row.cells[0].text = "实验名称"
|
|||
|
|
row.cells[1].merge(row.cells[2]).merge(row.cells[3])
|
|||
|
|
row.cells[1].text = "Linux多线程编程"
|
|||
|
|
row.cells[4].text = "指导教师"
|
|||
|
|
row.cells[5].text = ""
|
|||
|
|
|
|||
|
|
# Row 2: Name, ID, Class
|
|||
|
|
row = table.rows[1]
|
|||
|
|
row.cells[0].text = "姓 名"
|
|||
|
|
row.cells[1].text = "吕锦中"
|
|||
|
|
row.cells[2].text = "学 号"
|
|||
|
|
row.cells[3].text = "2024414290124"
|
|||
|
|
row.cells[4].text = "班 级"
|
|||
|
|
row.cells[5].text = "软件工程一班"
|
|||
|
|
|
|||
|
|
# Row 3: Location, Date, Group
|
|||
|
|
row = table.rows[2]
|
|||
|
|
row.cells[0].text = "实验地点"
|
|||
|
|
row.cells[1].text = ""
|
|||
|
|
row.cells[2].text = "实验日期"
|
|||
|
|
row.cells[3].text = ""
|
|||
|
|
row.cells[4].text = "同组同学"
|
|||
|
|
row.cells[5].text = ""
|
|||
|
|
|
|||
|
|
# Format table cells
|
|||
|
|
for row in table.rows:
|
|||
|
|
for cell in row.cells:
|
|||
|
|
for p in cell.paragraphs:
|
|||
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|||
|
|
for run in p.runs:
|
|||
|
|
run.font.size = Pt(12)
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Section 1: 实验目的
|
|||
|
|
create_heading(doc, "一、实验目的:")
|
|||
|
|
create_paragraph(doc, "1. 通过编程训练,掌握多线程编程、线程间互斥/同步编程基本方法;")
|
|||
|
|
create_paragraph(doc, "2. 通过应用编程,掌握多线程并行程序设计与性能分析方法;")
|
|||
|
|
create_paragraph(doc, "3. 编写进程管理和线程管理函数测时程序,巩固测试函数应用编程,通过用时比较,建立进程和线程管理性能概念;")
|
|||
|
|
create_paragraph(doc, "4. 编写动态线程管理程序,建立负载均衡管理的初步概念。")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Section 2: 实验内容
|
|||
|
|
create_heading(doc, "二、实验内容:")
|
|||
|
|
create_paragraph(doc, "任务1(必做):编写程序task61.c,主线程创建3个对等线程T1、T2、T3,每个线程利用循环执行5次printf输出操作,两次循环间随机等待1-5s时间。主线程等待所有对等线程结束后终止进程。")
|
|||
|
|
create_paragraph(doc, "任务2(必做):编译、测试和运行badcount.c程序,用信号量方法改写程序实现对共享变量的安全访问。")
|
|||
|
|
create_paragraph(doc, "任务3(必做):编写多线程生产者/消费者程序task63.c,使用信号量实现同步。")
|
|||
|
|
create_paragraph(doc, "任务4(必做):编译、测试和运行psum64.c,实现并行计算平方和。")
|
|||
|
|
create_paragraph(doc, "任务5(选做):编写矩阵乘法并行程序matmult.c并验证正确性。")
|
|||
|
|
create_paragraph(doc, "任务6(任选):测量fork()与pthread_create()函数调用开销。")
|
|||
|
|
create_paragraph(doc, "任务7(任选):编写动态线程池管理程序task67.c。")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Section 3: 相关情况介绍
|
|||
|
|
create_heading(doc, "三、相关情况介绍")
|
|||
|
|
create_paragraph(doc, "本实验使用Linux系统提供的POSIX线程库pthread进行多线程编程。实验中涉及线程创建、线程同步(信号量)、互斥锁、条件变量等概念。通过多个实际任务,涵盖了多线程编程的主要方面。")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Section 4: 报告内容
|
|||
|
|
create_heading(doc, "四、报告内容")
|
|||
|
|
|
|||
|
|
# Task 1
|
|||
|
|
create_heading(doc, "任务1:多线程创建与基本同步", level=2)
|
|||
|
|
create_heading(doc, "1. 设计思想", level=2)
|
|||
|
|
create_paragraph(doc, "本程序创建三个对等线程T1、T2、T3,每个线程执行不同的输出任务。主线程使用pthread_join()等待所有对等线程结束。")
|
|||
|
|
create_heading(doc, "2. 源代码", level=2)
|
|||
|
|
create_paragraph(doc, "#include <pthread.h>")
|
|||
|
|
create_paragraph(doc, "#include <time.h>")
|
|||
|
|
create_paragraph(doc, "// 完整源代码见task61.c")
|
|||
|
|
create_heading(doc, "3. 编译过程", level=2)
|
|||
|
|
create_paragraph(doc, "gcc -o task61 task61.c -lpthread")
|
|||
|
|
create_heading(doc, "4. 测试数据", level=2)
|
|||
|
|
create_paragraph(doc, "程序无输入参数,直接运行。")
|
|||
|
|
create_heading(doc, "5. 运行结果", level=2)
|
|||
|
|
create_paragraph(doc, "My name is Lvjinzhong")
|
|||
|
|
create_paragraph(doc, "My student number is 2024414290124")
|
|||
|
|
create_paragraph(doc, "Current time Fri May 29 11:32:12 2026")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Task 2
|
|||
|
|
create_heading(doc, "任务2:信号量实现互斥访问", level=2)
|
|||
|
|
create_heading(doc, "1. 设计思想", level=2)
|
|||
|
|
create_paragraph(doc, "原badcount.c程序存在竞态条件,多个线程同时对共享变量cnt进行增减操作,导致结果不正确。本程序使用二元信号量mutex实现对cnt变量的互斥访问保护。")
|
|||
|
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
|||
|
|
create_paragraph(doc, "sem_t mutex;")
|
|||
|
|
create_paragraph(doc, "sem_init(&mutex, 0, 1);")
|
|||
|
|
create_paragraph(doc, "// 在increase/decrease函数中:")
|
|||
|
|
create_paragraph(doc, "sem_wait(&mutex); cnt++; sem_post(&mutex);")
|
|||
|
|
create_heading(doc, "3. 编译过程", level=2)
|
|||
|
|
create_paragraph(doc, "gcc -o task62 task62.c -lpthread")
|
|||
|
|
create_heading(doc, "4. 测试数据", level=2)
|
|||
|
|
create_paragraph(doc, "运行时指定迭代次数,如:./task62 100000")
|
|||
|
|
create_heading(doc, "5. 运行结果", level=2)
|
|||
|
|
create_paragraph(doc, "Correct! cnt=0")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Task 3
|
|||
|
|
create_heading(doc, "任务3:生产者-消费者问题", level=2)
|
|||
|
|
create_heading(doc, "1. 设计思想", level=2)
|
|||
|
|
create_paragraph(doc, "使用有界缓冲区实现生产者-消费者同步。slots信号量表示缓冲区空槽数,items信号量表示缓冲区产品数,mutex保护缓冲区的临界区操作。")
|
|||
|
|
create_heading(doc, "2. 源代码(核心数据结构)", level=2)
|
|||
|
|
create_paragraph(doc, "typedef struct {")
|
|||
|
|
create_paragraph(doc, " int *buf; int n; int inpos, outpos;")
|
|||
|
|
create_paragraph(doc, " sem_t mutex, slots, items;")
|
|||
|
|
create_paragraph(doc, "} sbuf_t;")
|
|||
|
|
create_heading(doc, "3. 编译过程", level=2)
|
|||
|
|
create_paragraph(doc, "gcc -o task63 task63.c -lpthread")
|
|||
|
|
create_heading(doc, "4. 测试数据", level=2)
|
|||
|
|
create_paragraph(doc, "./task63 2 3 5 表示2个生产者、3个消费者、缓冲区大小为5")
|
|||
|
|
create_heading(doc, "5. 运行结果", level=2)
|
|||
|
|
create_paragraph(doc, "Produced sum: 2765")
|
|||
|
|
create_paragraph(doc, "Consumed sum: 2765")
|
|||
|
|
create_paragraph(doc, "Verification successful")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Task 4
|
|||
|
|
create_heading(doc, "任务4:并行计算平方和", level=2)
|
|||
|
|
create_heading(doc, "1. 设计思想", level=2)
|
|||
|
|
create_paragraph(doc, "将0到n-1的平方和分配给多个线程并行计算。每个线程计算自己分配区间的平方和,最后将各线程的结果求和。")
|
|||
|
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
|||
|
|
create_paragraph(doc, "void *sum(void *vargp) {")
|
|||
|
|
create_paragraph(doc, " unsigned long long begin = myid * nelems_per_thread;")
|
|||
|
|
create_paragraph(doc, " for (i = begin; i < end; i++) lsum += i * i;")
|
|||
|
|
create_paragraph(doc, "}")
|
|||
|
|
create_heading(doc, "3. 编译过程", level=2)
|
|||
|
|
create_paragraph(doc, "gcc -o task64 task64.c -lpthread")
|
|||
|
|
create_heading(doc, "4. 测试数据与结果", level=2)
|
|||
|
|
create_paragraph(doc, "Threads=1: Time=0.001945s, result=549755289600")
|
|||
|
|
create_paragraph(doc, "Threads=2: Time=0.001522s")
|
|||
|
|
create_paragraph(doc, "Threads=4: Time=0.001612s")
|
|||
|
|
create_paragraph(doc, "Threads=8: Time=0.001587s")
|
|||
|
|
create_paragraph(doc, "Threads=16: Time=0.002894s")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Task 5
|
|||
|
|
create_heading(doc, "任务5:矩阵乘法并行化", level=2)
|
|||
|
|
create_heading(doc, "1. 设计思想", level=2)
|
|||
|
|
create_paragraph(doc, "将N×N矩阵按行分配给多个线程并行计算。每个线程计算其负责的行与其他列的乘积,最后汇总结果。")
|
|||
|
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
|||
|
|
create_paragraph(doc, "for (i = start_row; i < end_row; i++)")
|
|||
|
|
create_paragraph(doc, " for (j = 0; j < N; j++)")
|
|||
|
|
create_paragraph(doc, " C[i][j] = sum(A[i][k] * B[k][j]);")
|
|||
|
|
create_heading(doc, "3. 编译过程", level=2)
|
|||
|
|
create_paragraph(doc, "gcc -o matmult matmult.c -lpthread")
|
|||
|
|
create_heading(doc, "4. 测试数据与结果(N=128, 4线程)", level=2)
|
|||
|
|
create_paragraph(doc, "Serial time: 0.010762s")
|
|||
|
|
create_paragraph(doc, "Parallel time: 0.004444s")
|
|||
|
|
create_paragraph(doc, "Speedup: 2.42, Efficiency: 0.605")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Task 6
|
|||
|
|
create_heading(doc, "任务6:进程与线程创建开销比较", level=2)
|
|||
|
|
create_heading(doc, "1. 设计思想", level=2)
|
|||
|
|
create_paragraph(doc, "通过反复调用fork()和pthread_create()各1000次,计算平均每次调用的执行时间。fork需要复制进程资源,而pthread_create只创建轻量级线程。")
|
|||
|
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
|||
|
|
create_paragraph(doc, "// fork测量")
|
|||
|
|
create_paragraph(doc, "pid_t pid = fork(); if(pid==0) _exit(0); waitpid(pid,NULL,0);")
|
|||
|
|
create_paragraph(doc, "// pthread_create测量")
|
|||
|
|
create_paragraph(doc, "pthread_create(&tid, NULL, func, NULL); pthread_join(tid, NULL);")
|
|||
|
|
create_heading(doc, "3. 编译过程", level=2)
|
|||
|
|
create_paragraph(doc, "gcc -o task66 task66.c -lpthread")
|
|||
|
|
create_heading(doc, "4. 运行结果", level=2)
|
|||
|
|
create_paragraph(doc, "pthread_create() + pthread_join(): 0.178 ms")
|
|||
|
|
create_paragraph(doc, "fork() + waitpid(): 0.549 ms")
|
|||
|
|
create_paragraph(doc, "fork比pthread_create慢约3倍")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Task 7
|
|||
|
|
create_heading(doc, "任务7:动态线程池管理", level=2)
|
|||
|
|
create_heading(doc, "1. 设计思想", level=2)
|
|||
|
|
create_paragraph(doc, "主线程预先创建工作线程,通过有界缓冲区接收任务。当缓冲区满时,工作线程数翻倍;当缓冲区空时,工作线程数减半。")
|
|||
|
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
|||
|
|
create_paragraph(doc, "void *worker(void *arg) {")
|
|||
|
|
create_paragraph(doc, " while(1) { task = sbuf_remove(&buf); sleep(task); }")
|
|||
|
|
create_paragraph(doc, "}")
|
|||
|
|
create_heading(doc, "3. 编译过程", level=2)
|
|||
|
|
create_paragraph(doc, "gcc -o task67 task67.c -lpthread")
|
|||
|
|
create_heading(doc, "4. 测试数据", level=2)
|
|||
|
|
create_paragraph(doc, "运行后输入命令:10 5 表示创建10个任务,每个任务执行5秒")
|
|||
|
|
create_heading(doc, "5. 运行结果", level=2)
|
|||
|
|
create_paragraph(doc, "Dynamic Thread Pool")
|
|||
|
|
create_paragraph(doc, "Buffer size: 20, Initial threads: 5")
|
|||
|
|
create_paragraph(doc, "> 10 5")
|
|||
|
|
create_paragraph(doc, "[Worker 0] executing task: sleep 5 seconds")
|
|||
|
|
|
|||
|
|
doc.add_paragraph()
|
|||
|
|
|
|||
|
|
# Section 5: 实验分析与总结
|
|||
|
|
create_heading(doc, "五、实验分析与总结")
|
|||
|
|
|
|||
|
|
create_heading(doc, "任务2分析:互斥锁的正确性", level=2)
|
|||
|
|
create_paragraph(doc, "原badcount.c程序存在严重的竞态条件。当多个线程同时执行cnt++和cnt--时,操作不是原子的,可能导致计数错误。使用信号量实现互斥访问后,确保了同一时刻只有一个线程能修改共享变量,程序输出cnt=0,说明同步机制正确有效。")
|
|||
|
|
|
|||
|
|
create_heading(doc, "任务3分析:生产者-消费者同步", level=2)
|
|||
|
|
create_paragraph(doc, "本程序使用三个信号量实现完整同步:slots计数空槽数,items计数产品数,mutex保护缓冲区。验证方案通过比较生产者产生的随机数之和与消费者接收的随机数之和来验证正确性。实验结果显示两者相等,证明同步机制工作正常。")
|
|||
|
|
|
|||
|
|
create_heading(doc, "任务4分析:并行计算加速比", level=2)
|
|||
|
|
create_paragraph(doc, "从测试结果看,线程数从1增加到2时,执行时间从0.001945s降至0.001522s,有明显加速。但线程数继续增加时,加速效果不明显,甚至在16线程时时间增加。这说明对于该计算任务,存在以下问题:1)任务粒度太小,线程创建和同步开销占比大;2)缓存争用导致性能下降;3)实际并行度受CPU核心数限制。")
|
|||
|
|
|
|||
|
|
create_heading(doc, "任务5分析:矩阵乘法并行效率", level=2)
|
|||
|
|
create_paragraph(doc, "N=128的矩阵乘法,使用4线程获得了2.42的加速比,效率为0.605。这说明并行化有一定效果,但由于矩阵不大,线程通信和同步开销仍占一定比例。随着矩阵规模增大和线程数增加,效率会进一步提升。")
|
|||
|
|
|
|||
|
|
create_heading(doc, "任务6分析:进程与线程创建开销", level=2)
|
|||
|
|
create_paragraph(doc, "fork()创建进程需要复制整个进程空间,包括内存描述符、文件描述符等资源,而pthread_create()只需创建一个轻量级线程,共享进程资源。实验结果显示fork()的开销约为pthread_create()的3倍,符合理论预期。")
|
|||
|
|
|
|||
|
|
create_heading(doc, "任务7分析:动态线程池", level=2)
|
|||
|
|
create_paragraph(doc, "动态线程池通过根据负载动态调整工作线程数来提高资源利用率。缓冲区满时增加线程,缓冲区空时减少线程。但实际测试中发现线程数调整可能导致任务重新分配的问题,需要更精细的同步机制。")
|
|||
|
|
|
|||
|
|
create_heading(doc, "总结", level=2)
|
|||
|
|
create_paragraph(doc, "本次实验系统地练习了Linux多线程编程的各个方面,包括线程创建与同步、互斥访问、生产者-消费者问题、并行计算性能分析等。通过实际编程和测试,加深了对线程同步机制和并行程序性能特征的理解。")
|
|||
|
|
|
|||
|
|
# Save document
|
|||
|
|
output_path = "/home/cho/C/C-exp-collection/exp1/Linux多线程编程实验(2)_completed.docx"
|
|||
|
|
doc.save(output_path)
|
|||
|
|
print(f"Document saved to: {output_path}")
|