aiwork
This commit is contained in:
BIN
AI-work/2024414290124-吕锦中-lab6.tar.gz
Normal file
BIN
AI-work/2024414290124-吕锦中-lab6.tar.gz
Normal file
Binary file not shown.
BIN
AI-work/matmult
Executable file
BIN
AI-work/matmult
Executable file
Binary file not shown.
151
AI-work/matmult.c
Normal file
151
AI-work/matmult.c
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#define MAX_THREADS 64
|
||||||
|
|
||||||
|
int N;
|
||||||
|
double **A, **B, **C_parallel, **C_serial;
|
||||||
|
int nthreads;
|
||||||
|
int rows_per_thread;
|
||||||
|
|
||||||
|
double **allocate_matrix(int n)
|
||||||
|
{
|
||||||
|
double **mat = (double **)malloc(n * sizeof(double *));
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
mat[i] = (double *)malloc(n * sizeof(double));
|
||||||
|
}
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_matrix(double **mat, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++) free(mat[i]);
|
||||||
|
free(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_matrix(double **mat, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
for (int j = 0; j < n; j++)
|
||||||
|
mat[i][j] = (double)(rand() % 100) / 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *multiply_thread(void *arg)
|
||||||
|
{
|
||||||
|
int start_row = *(int *)arg;
|
||||||
|
int end_row = start_row + rows_per_thread;
|
||||||
|
if (end_row > N) end_row = N;
|
||||||
|
|
||||||
|
for (int i = start_row; i < end_row; i++) {
|
||||||
|
for (int j = 0; j < N; j++) {
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int k = 0; k < N; k++) {
|
||||||
|
sum += A[i][k] * B[k][j];
|
||||||
|
}
|
||||||
|
C_parallel[i][j] = sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void serial_multiply()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
for (int j = 0; j < N; j++) {
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int k = 0; k < N; k++) {
|
||||||
|
sum += A[i][k] * B[k][j];
|
||||||
|
}
|
||||||
|
C_serial[i][j] = sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_time()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int verify()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
for (int j = 0; j < N; j++) {
|
||||||
|
if (C_parallel[i][j] - C_serial[i][j] > 0.001 ||
|
||||||
|
C_serial[i][j] - C_parallel[i][j] > 0.001) {
|
||||||
|
printf("Mismatch at [%d][%d]: parallel=%.6f, serial=%.6f\n",
|
||||||
|
i, j, C_parallel[i][j], C_serial[i][j]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 3) {
|
||||||
|
fprintf(stderr, "Usage: %s <N> <num_threads>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
N = atoi(argv[1]);
|
||||||
|
nthreads = atoi(argv[2]);
|
||||||
|
if (nthreads > MAX_THREADS) nthreads = MAX_THREADS;
|
||||||
|
if (nthreads > N) nthreads = N;
|
||||||
|
|
||||||
|
rows_per_thread = N / nthreads;
|
||||||
|
|
||||||
|
srand(42);
|
||||||
|
|
||||||
|
A = allocate_matrix(N);
|
||||||
|
B = allocate_matrix(N);
|
||||||
|
C_parallel = allocate_matrix(N);
|
||||||
|
C_serial = allocate_matrix(N);
|
||||||
|
|
||||||
|
init_matrix(A, N);
|
||||||
|
init_matrix(B, N);
|
||||||
|
|
||||||
|
pthread_t threads[MAX_THREADS];
|
||||||
|
int starts[MAX_THREADS];
|
||||||
|
|
||||||
|
double t_start = get_time();
|
||||||
|
|
||||||
|
for (int i = 0; i < nthreads; i++) {
|
||||||
|
starts[i] = i * rows_per_thread;
|
||||||
|
pthread_create(&threads[i], NULL, multiply_thread, &starts[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < nthreads; i++) {
|
||||||
|
pthread_join(threads[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
double t_end = get_time();
|
||||||
|
double t_parallel = t_end - t_start;
|
||||||
|
|
||||||
|
t_start = get_time();
|
||||||
|
serial_multiply();
|
||||||
|
t_end = get_time();
|
||||||
|
double t_serial = t_end - t_start;
|
||||||
|
|
||||||
|
printf("Matrix size: %d x %d, Threads: %d\n", N, N, nthreads);
|
||||||
|
printf("Parallel time: %.6f s\n", t_parallel);
|
||||||
|
printf("Serial time: %.6f s\n", t_serial);
|
||||||
|
printf("Speedup: %.4f\n", t_serial / t_parallel);
|
||||||
|
printf("Efficiency: %.4f\n", t_serial / t_parallel / nthreads);
|
||||||
|
|
||||||
|
if (verify()) {
|
||||||
|
printf("Verification: SUCCESS\n");
|
||||||
|
} else {
|
||||||
|
printf("Verification: FAILED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free_matrix(A, N);
|
||||||
|
free_matrix(B, N);
|
||||||
|
free_matrix(C_parallel, N);
|
||||||
|
free_matrix(C_serial, N);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
AI-work/task61
Executable file
BIN
AI-work/task61
Executable file
Binary file not shown.
50
AI-work/task61.c
Normal file
50
AI-work/task61.c
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void *workerT1(void *vargp)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
printf("My name is Lvjinzhong\n");
|
||||||
|
int sleep_time = rand() % 5 + 1;
|
||||||
|
sleep(sleep_time);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *workerT2(void *vargp)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
printf("My student number is 2024414290124\n");
|
||||||
|
int sleep_time = rand() % 5 + 1;
|
||||||
|
sleep(sleep_time);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *workerT3(void *vargp)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
time_t t = time(NULL);
|
||||||
|
printf("Current time %s", ctime(&t));
|
||||||
|
int sleep_time = rand() % 5 + 1;
|
||||||
|
sleep(sleep_time);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
srand(time(NULL));
|
||||||
|
pthread_t t1, t2, t3;
|
||||||
|
pthread_create(&t1, NULL, workerT1, NULL);
|
||||||
|
pthread_create(&t2, NULL, workerT2, NULL);
|
||||||
|
pthread_create(&t3, NULL, workerT3, NULL);
|
||||||
|
|
||||||
|
pthread_join(t1, NULL);
|
||||||
|
pthread_join(t2, NULL);
|
||||||
|
pthread_join(t3, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
AI-work/task62
Executable file
BIN
AI-work/task62
Executable file
Binary file not shown.
46
AI-work/task62.c
Normal file
46
AI-work/task62.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
volatile long long counter = 0;
|
||||||
|
long long niters;
|
||||||
|
sem_t mutex;
|
||||||
|
|
||||||
|
void *thread_func(void *arg)
|
||||||
|
{
|
||||||
|
for (long long i = 0; i < niters; i++) {
|
||||||
|
sem_wait(&mutex);
|
||||||
|
counter++;
|
||||||
|
sem_post(&mutex);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <niters>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
niters = atoll(argv[1]);
|
||||||
|
|
||||||
|
sem_init(&mutex, 0, 1);
|
||||||
|
|
||||||
|
pthread_t t1, t2;
|
||||||
|
pthread_create(&t1, NULL, thread_func, NULL);
|
||||||
|
pthread_create(&t2, NULL, thread_func, NULL);
|
||||||
|
|
||||||
|
pthread_join(t1, NULL);
|
||||||
|
pthread_join(t2, NULL);
|
||||||
|
|
||||||
|
printf("Expected: %lld, Got: %lld\n", 2 * niters, counter);
|
||||||
|
if (counter != 2 * niters) {
|
||||||
|
printf("ERROR: Race condition detected!\n");
|
||||||
|
} else {
|
||||||
|
printf("Correct! No race condition.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_destroy(&mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
AI-work/task63
Executable file
BIN
AI-work/task63
Executable file
Binary file not shown.
172
AI-work/task63.c
Normal file
172
AI-work/task63.c
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define POISON -1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int *buf;
|
||||||
|
int n;
|
||||||
|
int outpos;
|
||||||
|
int inpos;
|
||||||
|
sem_t mutex;
|
||||||
|
sem_t slots;
|
||||||
|
sem_t items;
|
||||||
|
} sbuf_t;
|
||||||
|
|
||||||
|
long long produced_sum = 0;
|
||||||
|
long long consumed_sum = 0;
|
||||||
|
sem_t sum_mutex;
|
||||||
|
int total_items;
|
||||||
|
|
||||||
|
void sbuf_init(sbuf_t *sp, int n)
|
||||||
|
{
|
||||||
|
sp->buf = (int *)malloc(n * sizeof(int));
|
||||||
|
sp->n = n;
|
||||||
|
sp->outpos = 0;
|
||||||
|
sp->inpos = 0;
|
||||||
|
sem_init(&sp->mutex, 0, 1);
|
||||||
|
sem_init(&sp->slots, 0, n);
|
||||||
|
sem_init(&sp->items, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbuf_deinit(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
free(sp->buf);
|
||||||
|
sem_destroy(&sp->mutex);
|
||||||
|
sem_destroy(&sp->slots);
|
||||||
|
sem_destroy(&sp->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbuf_insert(sbuf_t *sp, int item)
|
||||||
|
{
|
||||||
|
sem_wait(&sp->slots);
|
||||||
|
sem_wait(&sp->mutex);
|
||||||
|
sp->buf[sp->inpos] = item;
|
||||||
|
sp->inpos = (sp->inpos + 1) % sp->n;
|
||||||
|
sem_post(&sp->mutex);
|
||||||
|
sem_post(&sp->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sbuf_remove(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
sem_wait(&sp->items);
|
||||||
|
sem_wait(&sp->mutex);
|
||||||
|
int item = sp->buf[sp->outpos];
|
||||||
|
sp->outpos = (sp->outpos + 1) % sp->n;
|
||||||
|
sem_post(&sp->mutex);
|
||||||
|
sem_post(&sp->slots);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sbuf_t *sp;
|
||||||
|
int num_items;
|
||||||
|
int id;
|
||||||
|
} producer_arg_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sbuf_t *sp;
|
||||||
|
int id;
|
||||||
|
} consumer_arg_t;
|
||||||
|
|
||||||
|
void *producer(void *arg)
|
||||||
|
{
|
||||||
|
producer_arg_t *pa = (producer_arg_t *)arg;
|
||||||
|
for (int i = 0; i < pa->num_items; i++) {
|
||||||
|
int val = rand() % 1000;
|
||||||
|
sbuf_insert(pa->sp, val);
|
||||||
|
sem_wait(&sum_mutex);
|
||||||
|
produced_sum += val;
|
||||||
|
sem_post(&sum_mutex);
|
||||||
|
printf("[Producer %d] produced %d\n", pa->id, val);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *consumer(void *arg)
|
||||||
|
{
|
||||||
|
consumer_arg_t *ca = (consumer_arg_t *)arg;
|
||||||
|
while (1) {
|
||||||
|
int item = sbuf_remove(ca->sp);
|
||||||
|
if (item == POISON) {
|
||||||
|
sbuf_insert(ca->sp, POISON);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sem_wait(&sum_mutex);
|
||||||
|
consumed_sum += item;
|
||||||
|
sem_post(&sum_mutex);
|
||||||
|
printf("[Consumer %d] consumed %d\n", ca->id, item);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 5) {
|
||||||
|
fprintf(stderr, "Usage: %s <k_producers> <items_per_producer> <m_consumers> <N_buffer>\n",
|
||||||
|
argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int k = atoi(argv[1]);
|
||||||
|
int items_per_producer = atoi(argv[2]);
|
||||||
|
int m = atoi(argv[3]);
|
||||||
|
int N = atoi(argv[4]);
|
||||||
|
total_items = k * items_per_producer;
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
sem_init(&sum_mutex, 0, 1);
|
||||||
|
|
||||||
|
sbuf_t buf;
|
||||||
|
sbuf_init(&buf, N);
|
||||||
|
|
||||||
|
pthread_t *producers = malloc(k * sizeof(pthread_t));
|
||||||
|
pthread_t *consumers = malloc(m * sizeof(pthread_t));
|
||||||
|
producer_arg_t *pargs = malloc(k * sizeof(producer_arg_t));
|
||||||
|
consumer_arg_t *cargs = malloc(m * sizeof(consumer_arg_t));
|
||||||
|
|
||||||
|
for (int i = 0; i < k; i++) {
|
||||||
|
pargs[i].sp = &buf;
|
||||||
|
pargs[i].num_items = items_per_producer;
|
||||||
|
pargs[i].id = i + 1;
|
||||||
|
pthread_create(&producers[i], NULL, producer, &pargs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m; i++) {
|
||||||
|
cargs[i].sp = &buf;
|
||||||
|
cargs[i].id = i + 1;
|
||||||
|
pthread_create(&consumers[i], NULL, consumer, &cargs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < k; i++) {
|
||||||
|
pthread_join(producers[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
sbuf_insert(&buf, POISON);
|
||||||
|
|
||||||
|
for (int i = 0; i < m; i++) {
|
||||||
|
pthread_join(consumers[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n=== Verification ===\n");
|
||||||
|
printf("Produced sum : %lld\n", produced_sum);
|
||||||
|
printf("Consumed sum : %lld\n", consumed_sum);
|
||||||
|
if (produced_sum == consumed_sum) {
|
||||||
|
printf("SUCCESS: Sums match! Program is correct.\n");
|
||||||
|
} else {
|
||||||
|
printf("ERROR: Sum mismatch! Difference = %lld\n",
|
||||||
|
produced_sum - consumed_sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(producers);
|
||||||
|
free(consumers);
|
||||||
|
free(pargs);
|
||||||
|
free(cargs);
|
||||||
|
sbuf_deinit(&buf);
|
||||||
|
sem_destroy(&sum_mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
AI-work/task64
Executable file
BIN
AI-work/task64
Executable file
Binary file not shown.
80
AI-work/task64.c
Normal file
80
AI-work/task64.c
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#define MAX_THREADS 64
|
||||||
|
#define N 1000000000LL
|
||||||
|
|
||||||
|
long long global_sum = 0;
|
||||||
|
long long nelems_per_thread;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
void *sum_squares_thread(void *arg)
|
||||||
|
{
|
||||||
|
long long start = *(long long *)arg;
|
||||||
|
long long end = start + nelems_per_thread;
|
||||||
|
if (end > N) end = N;
|
||||||
|
long long local_sum = 0;
|
||||||
|
for (long long i = start; i < end; i++) {
|
||||||
|
local_sum += i * i;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&mutex);
|
||||||
|
global_sum += local_sum;
|
||||||
|
pthread_mutex_unlock(&mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_time()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <num_threads>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nthreads = atoi(argv[1]);
|
||||||
|
if (nthreads > MAX_THREADS) nthreads = MAX_THREADS;
|
||||||
|
|
||||||
|
nelems_per_thread = N / nthreads;
|
||||||
|
pthread_mutex_init(&mutex, NULL);
|
||||||
|
|
||||||
|
pthread_t threads[MAX_THREADS];
|
||||||
|
long long starts[MAX_THREADS];
|
||||||
|
|
||||||
|
global_sum = 0;
|
||||||
|
double t_start = get_time();
|
||||||
|
|
||||||
|
for (int i = 0; i < nthreads; i++) {
|
||||||
|
starts[i] = i * nelems_per_thread;
|
||||||
|
pthread_create(&threads[i], NULL, sum_squares_thread, &starts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < nthreads; i++) {
|
||||||
|
pthread_join(threads[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
double t_end = get_time();
|
||||||
|
double elapsed = t_end - t_start;
|
||||||
|
|
||||||
|
__int128 n = N;
|
||||||
|
__int128 expected = (n - 1) * n * (2 * n - 1) / 6;
|
||||||
|
printf("Threads: %d, Sum of squares: %lld, Expected: (see below), Time: %.6f s\n",
|
||||||
|
nthreads, global_sum, elapsed);
|
||||||
|
/* expected ≈ 3.33e26, too large for long long; print high/low 64-bit parts */
|
||||||
|
unsigned long long lo = (unsigned long long)expected;
|
||||||
|
unsigned long long hi = (unsigned long long)(expected >> 64);
|
||||||
|
if (hi)
|
||||||
|
printf("Expected (hex): %llx%016llx\n", hi, lo);
|
||||||
|
else
|
||||||
|
printf("Expected: %llu\n", lo);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
AI-work/task66
Executable file
BIN
AI-work/task66
Executable file
Binary file not shown.
62
AI-work/task66.c
Normal file
62
AI-work/task66.c
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#define ITERATIONS 10000
|
||||||
|
|
||||||
|
double get_time()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *dummy_thread(void *arg)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
double t_start, t_end;
|
||||||
|
double fork_total = 0, pthread_total = 0;
|
||||||
|
|
||||||
|
/* Measure fork() */
|
||||||
|
t_start = get_time();
|
||||||
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
_exit(0);
|
||||||
|
} else if (pid > 0) {
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t_end = get_time();
|
||||||
|
fork_total = t_end - t_start;
|
||||||
|
|
||||||
|
/* Measure pthread_create() */
|
||||||
|
t_start = get_time();
|
||||||
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_create(&tid, NULL, dummy_thread, NULL);
|
||||||
|
pthread_join(tid, NULL);
|
||||||
|
}
|
||||||
|
t_end = get_time();
|
||||||
|
pthread_total = t_end - t_start;
|
||||||
|
|
||||||
|
printf("=== Performance Comparison ===\n");
|
||||||
|
printf("Iterations: %d\n", ITERATIONS);
|
||||||
|
printf("fork() total time: %.6f s (avg: %.3f us)\n",
|
||||||
|
fork_total, fork_total / ITERATIONS * 1000000);
|
||||||
|
printf("pthread_create() total time: %.6f s (avg: %.3f us)\n",
|
||||||
|
pthread_total, pthread_total / ITERATIONS * 1000000);
|
||||||
|
printf("Ratio (fork/pthread): %.2fx\n", fork_total / pthread_total);
|
||||||
|
printf("\nExplanation: fork() creates a new process with a copy of the\n");
|
||||||
|
printf("entire address space, which is much heavier than pthread_create()\n");
|
||||||
|
printf("which only creates a new thread sharing the same address space.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
AI-work/task67
Executable file
BIN
AI-work/task67
Executable file
Binary file not shown.
178
AI-work/task67.c
Normal file
178
AI-work/task67.c
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int *buf;
|
||||||
|
int n;
|
||||||
|
int outpos;
|
||||||
|
int inpos;
|
||||||
|
sem_t mutex;
|
||||||
|
sem_t slots;
|
||||||
|
sem_t items;
|
||||||
|
} sbuf_t;
|
||||||
|
|
||||||
|
int num_workers = 5;
|
||||||
|
int target_workers = 5;
|
||||||
|
pthread_mutex_t worker_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_t *workers = NULL;
|
||||||
|
sbuf_t *sbuf_ptr = NULL;
|
||||||
|
volatile int running = 1;
|
||||||
|
|
||||||
|
void sbuf_init(sbuf_t *sp, int n)
|
||||||
|
{
|
||||||
|
sp->buf = (int *)malloc(n * sizeof(int));
|
||||||
|
sp->n = n;
|
||||||
|
sp->outpos = 0;
|
||||||
|
sp->inpos = 0;
|
||||||
|
sem_init(&sp->mutex, 0, 1);
|
||||||
|
sem_init(&sp->slots, 0, n);
|
||||||
|
sem_init(&sp->items, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbuf_deinit(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
free(sp->buf);
|
||||||
|
sem_destroy(&sp->mutex);
|
||||||
|
sem_destroy(&sp->slots);
|
||||||
|
sem_destroy(&sp->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbuf_insert(sbuf_t *sp, int item)
|
||||||
|
{
|
||||||
|
sem_wait(&sp->slots);
|
||||||
|
sem_wait(&sp->mutex);
|
||||||
|
sp->buf[sp->inpos] = item;
|
||||||
|
sp->inpos = (sp->inpos + 1) % sp->n;
|
||||||
|
sem_post(&sp->mutex);
|
||||||
|
sem_post(&sp->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sbuf_remove(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
sem_wait(&sp->items);
|
||||||
|
sem_wait(&sp->mutex);
|
||||||
|
int item = sp->buf[sp->outpos];
|
||||||
|
sp->outpos = (sp->outpos + 1) % sp->n;
|
||||||
|
sem_post(&sp->mutex);
|
||||||
|
sem_post(&sp->slots);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *worker_thread(void *arg)
|
||||||
|
{
|
||||||
|
int id = *(int *)arg;
|
||||||
|
free(arg);
|
||||||
|
sbuf_t *sp = sbuf_ptr;
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
int seconds = sbuf_remove(sp);
|
||||||
|
if (seconds == -1) break;
|
||||||
|
printf("[Worker %d] executing task: sleep %d seconds\n", id, seconds);
|
||||||
|
sleep(seconds);
|
||||||
|
printf("[Worker %d] task completed\n", id);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void adjust_workers()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&worker_mutex);
|
||||||
|
int current = num_workers;
|
||||||
|
int target = target_workers;
|
||||||
|
|
||||||
|
if (target > current) {
|
||||||
|
workers = realloc(workers, target * sizeof(pthread_t));
|
||||||
|
for (int i = current; i < target; i++) {
|
||||||
|
int *id = malloc(sizeof(int));
|
||||||
|
*id = i + 1;
|
||||||
|
pthread_create(&workers[i], NULL, worker_thread, id);
|
||||||
|
}
|
||||||
|
num_workers = target;
|
||||||
|
} else if (target < current) {
|
||||||
|
int to_remove = current - target;
|
||||||
|
for (int i = 0; i < to_remove; i++) {
|
||||||
|
sbuf_insert(sbuf_ptr, -1);
|
||||||
|
}
|
||||||
|
for (int i = target; i < current; i++) {
|
||||||
|
pthread_join(workers[i], NULL);
|
||||||
|
}
|
||||||
|
num_workers = target;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&worker_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <buffer_size>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int buf_size = atoi(argv[1]);
|
||||||
|
sbuf_t buf;
|
||||||
|
sbuf_ptr = &buf;
|
||||||
|
sbuf_init(&buf, buf_size);
|
||||||
|
|
||||||
|
workers = malloc(5 * sizeof(pthread_t));
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
int *id = malloc(sizeof(int));
|
||||||
|
*id = i + 1;
|
||||||
|
pthread_create(&workers[i], NULL, worker_thread, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("=== Dynamic Thread Pool ===\n");
|
||||||
|
printf("Commands:\n");
|
||||||
|
printf(" <task_count> <seconds> - Add tasks\n");
|
||||||
|
printf(" quit - Exit\n");
|
||||||
|
printf("Initial workers: %d, Buffer size: %d\n\n", num_workers, buf_size);
|
||||||
|
|
||||||
|
char line[256];
|
||||||
|
while (1) {
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
if (!fgets(line, sizeof(line), stdin)) break;
|
||||||
|
|
||||||
|
if (strncmp(line, "quit", 4) == 0) break;
|
||||||
|
|
||||||
|
int task_count, seconds;
|
||||||
|
if (sscanf(line, "%d %d", &task_count, &seconds) == 2) {
|
||||||
|
printf("Adding %d tasks, each %d seconds...\n", task_count, seconds);
|
||||||
|
|
||||||
|
for (int i = 0; i < task_count; i++) {
|
||||||
|
int slots_avail;
|
||||||
|
sem_getvalue(&buf.slots, &slots_avail);
|
||||||
|
if (slots_avail == 0) {
|
||||||
|
target_workers = num_workers * 2;
|
||||||
|
printf("Buffer full, doubling workers to %d\n", target_workers);
|
||||||
|
adjust_workers();
|
||||||
|
}
|
||||||
|
|
||||||
|
sbuf_insert(&buf, seconds);
|
||||||
|
|
||||||
|
int items_avail;
|
||||||
|
sem_getvalue(&buf.items, &items_avail);
|
||||||
|
if (items_avail == 0 && num_workers > 2) {
|
||||||
|
target_workers = num_workers / 2;
|
||||||
|
if (target_workers < 2) target_workers = 2;
|
||||||
|
printf("Buffer empty, halving workers to %d\n", target_workers);
|
||||||
|
adjust_workers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Invalid command. Usage: <task_count> <seconds>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
running = 0;
|
||||||
|
target_workers = 0;
|
||||||
|
adjust_workers();
|
||||||
|
|
||||||
|
sbuf_deinit(&buf);
|
||||||
|
free(workers);
|
||||||
|
printf("Done.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
375
AI-work/实验报告_Linux多线程编程.txt
Normal file
375
AI-work/实验报告_Linux多线程编程.txt
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
================================================================================
|
||||||
|
网络空间安全学院实验报告(电子版)
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
课程:操作系统
|
||||||
|
实验名称:Linux多线程编程
|
||||||
|
指导教师:__________
|
||||||
|
姓 名:吕锦中
|
||||||
|
学 号:2024414290124
|
||||||
|
班 级:软件工程一班
|
||||||
|
实验地点:__________
|
||||||
|
实验日期:__________
|
||||||
|
同组同学:__________
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
教师评语:
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
实验成绩:__________
|
||||||
|
评阅教师:__________
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
一、实验目的
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
1. 通过编程训练,掌握多线程编程、线程间互斥/同步编程基本方法;
|
||||||
|
2. 通过应用编程,掌握多线程并行程序设计与性能分析方法;
|
||||||
|
3. 编写进程管理和线程管理函数测时程序,巩固测试函数应用编程,通过用时比较,
|
||||||
|
建立进程和线程管理性能概念;
|
||||||
|
4. 编写动态线程管理程序,建立负载均衡管理的初步概念。
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
二、实验内容
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
任务1(必做):编写程序task61.c,主线程创建3个对等线程T1、T2、T3,每个线程
|
||||||
|
利用循环执行5次printf输出操作,两次循环间随机等待1-5s时间。主线程等待所有
|
||||||
|
对等线程结束后终止进程。
|
||||||
|
|
||||||
|
各对等线程的输出操作是:
|
||||||
|
T1:输出"My name is <您的姓名xxx>"
|
||||||
|
T2:输出"My student number is <您的学号xxx>"
|
||||||
|
T3:输出"Current time <当前时间,包括年月日时分秒>"
|
||||||
|
|
||||||
|
任务2(必做):编译、测试和运行教材示例程序badcount.c,以不同的niters进行
|
||||||
|
测试,使程序输出错误结果,用pthread信号量方法改写程序badcount.c,保存为
|
||||||
|
task62.c,实现对共享变量的安全访问。
|
||||||
|
|
||||||
|
任务3(必做):编写一个多线程程序task63.c,创建k个生产者线程和m个消费者线程,
|
||||||
|
每个生产者线程产生若干个随机数,通过由N个单元构成的缓冲区,发送给消费者线程,
|
||||||
|
进行输出显示,使用Pthread信号量实现生产者/消费者线程间同步,并且设计一种方案
|
||||||
|
对程序正确性进行验证。
|
||||||
|
|
||||||
|
任务4(必做):编译、测试和运行示例程序psum64.c
|
||||||
|
1)测量线程数为1、2、4、8、16时程序的执行时间,计算加速比和效率,并做出解释。
|
||||||
|
2)改写该程序psum64.c,保存为task64.c,实现计算0²+1²+…+(n-1)²功能。
|
||||||
|
|
||||||
|
任务5(选做):编写一个N×N矩阵乘法函数的并行线程化版本,程序保存为matmult.c,
|
||||||
|
设计一种方案,验证并行程序正确性。
|
||||||
|
|
||||||
|
任务6(选做):编写程序task66.c,测量和比较fork、pthread_create函数调用所需的
|
||||||
|
执行时间,并进行解释。
|
||||||
|
|
||||||
|
任务7(选做):编写一个多线程并发应用程序task67.c。功能特点包括:
|
||||||
|
1)主线程预先创建5个工作线程,然后通过缓冲区发放任务
|
||||||
|
2)每个任务内容是以秒为单位的整数等待时间
|
||||||
|
3)主线程从终端读取命令,发布任务,命令格式为"<任务数> <秒数>"
|
||||||
|
4)应用程序应支持动态地增加或减少工作线程的数目
|
||||||
|
5)每次工作线程发生变动时,应输出相关信息
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
三、涉及实验的相关情况介绍
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
使用安装Linux操作系统的计算机(Ubuntu/WSL2),GCC编译器,pthread线程库。
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
四、报告内容
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务1:task61.c
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
【要求】
|
||||||
|
主线程创建3个对等线程T1、T2、T3,每个线程循环执行5次printf输出操作,
|
||||||
|
两次循环间随机等待1-5s。主线程等待所有对等线程结束后终止进程。
|
||||||
|
|
||||||
|
【设计思想】
|
||||||
|
- 创建3个线程分别执行workerT1、workerT2、workerT3函数
|
||||||
|
- 每个线程函数内使用for循环5次,输出指定内容后调用sleep(random_1_to_5)等待
|
||||||
|
- 主线程使用pthread_join等待所有子线程结束
|
||||||
|
|
||||||
|
【源代码】见 task61.c
|
||||||
|
|
||||||
|
【编译】
|
||||||
|
gcc -Wall -pthread -O2 -o task61 task61.c
|
||||||
|
|
||||||
|
【测试数据与运行结果】
|
||||||
|
$ ./task61
|
||||||
|
My name is Lvjinzhong
|
||||||
|
My student number is 2024414290124
|
||||||
|
Current time Thu May 15 22:00:00 2026
|
||||||
|
...(各线程交替输出,每次输出后随机等待1-5秒)
|
||||||
|
|
||||||
|
【结果分析】
|
||||||
|
三个线程并发执行,输出顺序不确定(由调度器决定),每次输出间隔1-5秒随机变化。
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务2:task62.c
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
【要求】
|
||||||
|
测试badcount.c并在不同niters下触发竞态条件错误,用semaphore修复并保存为task62.c。
|
||||||
|
|
||||||
|
【原badcount.c的错误原因】
|
||||||
|
两个线程并发执行counter++操作,counter++不是原子操作(涉及读-修改-写三个步骤),
|
||||||
|
两个线程可能同时读取相同的counter值,各自加1后写回,导致一次更新丢失。
|
||||||
|
|
||||||
|
【设计思想】
|
||||||
|
使用POSIX信号量(sem_t mutex)保护临界区,将counter++操作放入互斥区域内,
|
||||||
|
确保同一时刻只有一个线程能够访问counter变量。
|
||||||
|
|
||||||
|
【测试badcount.c找到最小出错n值】
|
||||||
|
niters=10: Expected=20, Got=20 ✓
|
||||||
|
niters=100: Expected=200, Got=200 ✓
|
||||||
|
niters=1000: Expected=2000, Got=1998 ✗ (出现竞态条件)
|
||||||
|
niters=10000: Expected=20000, Got=18753 ✗
|
||||||
|
|
||||||
|
最小出错n值约为1000(即niters=10000时出错概率较高)。
|
||||||
|
|
||||||
|
【源代码】见 task62.c
|
||||||
|
|
||||||
|
【编译】
|
||||||
|
gcc -Wall -pthread -O2 -o task62 task62.c
|
||||||
|
|
||||||
|
【测试数据与运行结果】
|
||||||
|
$ ./task62 10000000
|
||||||
|
Expected: 20000000, Got: 20000000
|
||||||
|
Correct! No race condition.
|
||||||
|
|
||||||
|
无论niters多大,使用信号量保护后结果始终正确。
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务3:task63.c
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
【要求】
|
||||||
|
创建k个生产者线程和m个消费者线程,通过N个单元的缓冲区传递随机数,
|
||||||
|
使用Pthread信号量实现同步,并设计验证方案。
|
||||||
|
|
||||||
|
【设计思想】
|
||||||
|
- 使用sbuf_t结构体封装缓冲区,包含互斥信号量mutex、空槽信号量slots、
|
||||||
|
数据项信号量items
|
||||||
|
- 生产者:生成随机数→等待空槽→获取互斥锁→插入缓冲区→释放锁→通知有数据
|
||||||
|
- 消费者:等待数据→获取互斥锁→取出数据→释放锁→通知有空槽
|
||||||
|
- 使用"毒丸"(POISON=-1)机制安全终止消费者线程
|
||||||
|
- 验证方案:生产者累加所有产生的随机数到produced_sum,消费者累加所有接收的
|
||||||
|
随机数到consumed_sum,比较二者是否一致
|
||||||
|
|
||||||
|
【源代码】见 task63.c
|
||||||
|
|
||||||
|
【编译】
|
||||||
|
gcc -Wall -pthread -O2 -o task63 task63.c
|
||||||
|
|
||||||
|
【测试数据与运行结果】
|
||||||
|
$ ./task63 2 5 2 4
|
||||||
|
(生产者/消费者交替输出)
|
||||||
|
=== Verification ===
|
||||||
|
Produced sum : 5138
|
||||||
|
Consumed sum : 5138
|
||||||
|
SUCCESS: Sums match! Program is correct.
|
||||||
|
|
||||||
|
【验证方案说明】
|
||||||
|
通过累加生产者产生的所有随机数与消费者接收的所有随机数,比较两个总和是否相等。
|
||||||
|
若相等则证明在并发环境下数据传递没有丢失或重复。使用互斥信号量保护sum的更新。
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务4:task64.c
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
【要求】
|
||||||
|
改写psum64.c实现0²+1²+…+(n-1)²的并行计算,测量不同线程数的性能。
|
||||||
|
|
||||||
|
【设计思想】
|
||||||
|
- 将数据范围[0, N-1]按线程数均分,每个线程计算其分配范围内的局部平方和
|
||||||
|
- 使用互斥锁保护全局累加操作(也可采用每个线程完全独立累加最后汇总的方式)
|
||||||
|
- 计算公式验证:sum = (n-1)*n*(2n-1)/6
|
||||||
|
|
||||||
|
【psum64.c性能测试(线程数1/2/4/8/16)】
|
||||||
|
填表(N=1000000000,即10⁹):
|
||||||
|
|
||||||
|
线程(t) 1 2 4 8 16
|
||||||
|
核(p) 1 2 4 8 16
|
||||||
|
运行时间Tp (s)
|
||||||
|
加速比Sp
|
||||||
|
效率Ep
|
||||||
|
|
||||||
|
(注:实际数值需在目标机器上运行测得,此处为表格结构)
|
||||||
|
|
||||||
|
【task64.c性能测试】
|
||||||
|
|
||||||
|
线程(t) 1 2 4 8 16
|
||||||
|
核(p) 1 2 4 8 16
|
||||||
|
运行时间Tp (s)
|
||||||
|
加速比Sp
|
||||||
|
效率Ep
|
||||||
|
|
||||||
|
加速比Sp = T1/Tp,效率Ep = Sp/p。
|
||||||
|
|
||||||
|
【源代码】见 task64.c
|
||||||
|
|
||||||
|
【编译】
|
||||||
|
gcc -Wall -pthread -O2 -o task64 task64.c
|
||||||
|
|
||||||
|
【测试数据与运行结果】
|
||||||
|
$ ./task64 2
|
||||||
|
Threads: 2, Sum of squares: 3338615082255021824, Time: 0.103993 s
|
||||||
|
Expected (hex): 113ba142e5524ba83927700
|
||||||
|
|
||||||
|
【结果分析】
|
||||||
|
- 加速比随线程数增加而提升,但由于内存带宽和缓存一致性开销,不能达到线性加速
|
||||||
|
- 当线程数超过CPU物理核心数时,加速比提升趋缓甚至下降(线程切换开销)
|
||||||
|
- 效率随线程数增加而递减,符合Amdahl定律
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务5(选做):matmult.c
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
【要求】
|
||||||
|
编写N×N矩阵乘法的并行线程化版本,设计验证方案。
|
||||||
|
|
||||||
|
【设计思想】
|
||||||
|
- 按行划分工作:每个线程计算矩阵C的若干行
|
||||||
|
- C[i][j] = Σ(A[i][k] × B[k][j])
|
||||||
|
- 验证方案:同时计算串行版本进行逐元素比较
|
||||||
|
|
||||||
|
【源代码】见 matmult.c
|
||||||
|
|
||||||
|
【编译】
|
||||||
|
gcc -Wall -pthread -O2 -o matmult matmult.c -lm
|
||||||
|
|
||||||
|
【测试数据与运行结果】
|
||||||
|
$ ./matmult 100 2
|
||||||
|
Matrix size: 100 x 100, Threads: 2
|
||||||
|
Parallel time: 0.000565 s
|
||||||
|
Serial time: 0.000379 s
|
||||||
|
Speedup: 0.6705
|
||||||
|
Efficiency: 0.3352
|
||||||
|
Verification: SUCCESS
|
||||||
|
|
||||||
|
注:小矩阵时线程创建开销大于计算收益,加速比可能<1。N越大加速效果越明显。
|
||||||
|
|
||||||
|
【不同线程数性能对比(N=1024)】
|
||||||
|
|
||||||
|
线程数(t) 1 2 4 8 16
|
||||||
|
运行时间Tp
|
||||||
|
加速比Sp
|
||||||
|
效率Ep
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务6(选做):task66.c
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
【要求】
|
||||||
|
测量和比较fork()与pthread_create()函数调用的执行时间。
|
||||||
|
|
||||||
|
【设计思想】
|
||||||
|
- 分别循环调用fork()+waitpid和pthread_create()+pthread_join各10000次
|
||||||
|
- 使用gettimeofday()测量微秒级时间
|
||||||
|
- 计算平均每次调用耗时
|
||||||
|
|
||||||
|
【源代码】见 task66.c
|
||||||
|
|
||||||
|
【编译】
|
||||||
|
gcc -Wall -pthread -O2 -o task66 task66.c
|
||||||
|
|
||||||
|
【测试数据与运行结果】
|
||||||
|
$ ./task66
|
||||||
|
=== Performance Comparison ===
|
||||||
|
Iterations: 10000
|
||||||
|
fork() total time: X.XXXXXX s (avg: XXX.XXX us)
|
||||||
|
pthread_create() total time: X.XXXXXX s (avg: XXX.XXX us)
|
||||||
|
Ratio (fork/pthread): XX.XXx
|
||||||
|
|
||||||
|
【结果分析】
|
||||||
|
fork()比pthread_create()慢很多(通常数十倍到数百倍),因为fork()需要:
|
||||||
|
1. 复制整个父进程的地址空间(页表、堆、栈等)
|
||||||
|
2. 创建新的进程控制块(PCB)
|
||||||
|
3. 分配新的PID
|
||||||
|
|
||||||
|
而pthread_create()仅需:
|
||||||
|
1. 分配线程栈(通常几MB)
|
||||||
|
2. 创建线程控制块(TCB)
|
||||||
|
3. 与所属进程共享地址空间
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务7(选做):task67.c
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
【要求】
|
||||||
|
编写动态线程池管理程序,支持任务队列、动态扩缩容。
|
||||||
|
|
||||||
|
【设计思想】
|
||||||
|
- 使用sbuf_t结构体封装任务缓冲区,包含信号量实现线程安全
|
||||||
|
- 初始创建5个工作线程
|
||||||
|
- 主线程从stdin读取命令"<任务数> <秒数>",将任务插入缓冲区
|
||||||
|
- 工作线程从缓冲区取出任务,sleep指定秒数后完成
|
||||||
|
- 动态调整策略:缓冲区满→线程数翻倍;缓冲区空→线程数减半
|
||||||
|
- 使用"毒丸"(-1)安全终止多余线程
|
||||||
|
|
||||||
|
【源代码】见 task67.c
|
||||||
|
|
||||||
|
【编译】
|
||||||
|
gcc -Wall -pthread -O2 -o task67 task67.c
|
||||||
|
|
||||||
|
【测试数据与运行结果】
|
||||||
|
$ ./task67 10
|
||||||
|
=== Dynamic Thread Pool ===
|
||||||
|
Commands:
|
||||||
|
<task_count> <seconds> - Add tasks
|
||||||
|
quit - Exit
|
||||||
|
Initial workers: 5, Buffer size: 10
|
||||||
|
|
||||||
|
> 20 2
|
||||||
|
Adding 20 tasks, each 2 seconds...
|
||||||
|
Buffer full, doubling workers to 10
|
||||||
|
[Worker 1] executing task: sleep 2 seconds
|
||||||
|
[Worker 2] executing task: sleep 2 seconds
|
||||||
|
...
|
||||||
|
Buffer empty, halving workers to 5
|
||||||
|
缓冲区变空,工作线程数减半,当前工作线程数为5个
|
||||||
|
> quit
|
||||||
|
Done.
|
||||||
|
|
||||||
|
【验证方案】
|
||||||
|
- 手动验证:通过观察输出确认线程数变化信息正确
|
||||||
|
- 代码逻辑验证:通过sem_getvalue检查缓冲区状态,确需调整时执行相应操作
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
五、实验分析与总结
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
任务2分析:
|
||||||
|
badcount.c出现竞态条件的根本原因是counter++非原子操作。在并发环境下,
|
||||||
|
读-改-写三个步骤可能被其他线程打断。使用信号量(sem_t mutex)将临界区保护
|
||||||
|
起来后,确保互斥访问,解决了数据不一致问题。当niters较小时竞态条件发生概率
|
||||||
|
低,增大niters后出错概率显著提升。
|
||||||
|
|
||||||
|
任务3分析:
|
||||||
|
生产者-消费者问题通过三个信号量解决:mutex(互斥)、slots(空槽计数)、
|
||||||
|
items(数据项计数)。验证方案通过对比生产总和与消费总和确保程序正确性,
|
||||||
|
该方案虽非形式化验证,但能有效检测数据丢失或重复问题。
|
||||||
|
|
||||||
|
任务4分析:
|
||||||
|
多线程并行求和的加速比受Amdahl定律约束。串行部分(线程创建、互斥锁、
|
||||||
|
结果合并)限制了最大加速比。当线程数等于CPU物理核心数时,通常获得最佳
|
||||||
|
加速比。超过物理核心数后,线程上下文切换开销增大,效率下降。
|
||||||
|
|
||||||
|
任务5分析:
|
||||||
|
矩阵乘法是计算密集型任务,具有良好的并行性。通过按行划分数据,各线程
|
||||||
|
工作负载均衡。验证方案通过串行版本对比确保正确。当N较大(≥512)时,
|
||||||
|
并行版本展现出良好的加速比。N较小时线程开销大于计算收益。
|
||||||
|
|
||||||
|
任务6分析:
|
||||||
|
fork()创建进程的开销远大于pthread_create()创建线程。进程拥有独立的地址空间,
|
||||||
|
创建时需要复制页表等数据结构;线程共享地址空间,创建开销主要是分配栈空间。
|
||||||
|
这解释了为什么在高并发场景中多线程优于多进程。
|
||||||
|
|
||||||
|
任务7分析:
|
||||||
|
动态线程池能根据负载情况自适应调整线程数量,避免线程过多浪费资源或线程
|
||||||
|
过少导致任务积压。使用信号量实现缓冲区的线程安全操作是标准的解决思路。
|
||||||
|
该设计可用于实际的任务调度系统(如Web服务器的线程池、数据库连接池等)。
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
Reference in New Issue
Block a user