一、俩demo
1.1 多线程完成图片复制
- 利用多线程完成图片复制,主线程复制前一半,子线程复制后一半
- 这个在之前多进程复制时已经有了相对完备的思想了,所以就不介绍具体思路了,差不多的思路
1.2 多线程翻转字符串与输出字符串
要求如下:
- 定义一个全局变量 char buf[] = “1234567”,创建两个线程,不考虑退出条件。
- A线程循环打印buf字符串,
- B线程循环倒置buf字符串,即buf中本来存储1234567,倒置后buf中存储7654321. B线程中不打印!!
- 倒置不允许使用辅助数组。
- 要求A线程打印出来的结果只能为 1234567 或者 7654321 不允许出现7634521 7234567
- 不允许使用sleep函数
思路:
- 一开始就想着用标志信号的方式完成,标志信号是通过 pthread_create 函数中的参数传到子进程的
- 主线程中循环打印字符串
- 子线程中循环将字符串翻转
- 下边是实现的思路
- 标志信号初始值我设定的为 0
- 打印时检查标志信号是否为 0,若为 0 则进行打印,打印完再将标志信号更改为 1 ,等待翻转时进行使用
- 翻转时检查标志信号是否为 1 ,若为 1 则进行翻转,翻转完再将标志信号改为 0 ,等待打印时使用
- 这种检查完标志信号是否符合再进行操作,操作完再进行修改标志信号,能够在一定程度上保证操作的原子性(即不可分割性)
二、多线程图片复制的代码
#include <my_head.h>
/*
利用线程复制图片,主线程复制前一半,子线程复制后一半
*/
void *half_copy(void *offset);
int main(int argc, const char *argv[])
{
// 修改文件掩码,并创建复制后的目的文件,若存在则清空,不存在则创建
umask(0);
int dest = open("copy.png", O_WRONLY | O_CREAT | O_TRUNC, 0664);
close(dest);
// 打开两个文件,读源文件,写目的文件
int src1 = open("1.png", O_RDONLY);
int dest1 = open("copy.png", O_WRONLY);
// 获取源文件的一半偏移量
off_t offset = lseek(src1, 0, SEEK_END);
offset /= 2;
// 再将偏移量移回文件开头
lseek(src1, 0, SEEK_SET);
// printf("主线程offset = %ld\n", offset);
// 创建子线程,子线程号为tid
pthread_t tid;
if (0 != pthread_create(&tid, NULL, half_copy, &offset))
{
fprintf(stderr, "pthread_create error __%d__\n", __LINE__);
return -1;
}
// 创建用于过度的存储变量
char temp[128];
// 接收读取的字节数返回值
ssize_t res;
while (1)
{
// 清空过度存储变量
bzero(temp, sizeof(temp));
// 判断剩余偏移量是否比过度存储容量大
// 若大于等于过度存储容量,则用过度存储容量的大小进行读取写入
// 若小于,则用剩余偏移量的大小进行读取写入
if (offset >= sizeof(temp))
{
res = read(src1, temp, sizeof(temp));
// 成功读取则写入,否则结束循环
if (0 < res)
{
write(dest1, temp, res);
}
// 没读到数据但成功执行读取函数则退出
else if (0 == res)
{
// 输出提示
printf("主线程中----图片复制完成\n");
break;
}
// 执行读取函数失败则返回NULL
else
{
ERR_MSG("read file error");
return -1;
}
}
else
{
printf("主线程读取到达边缘\n");
res = read(src1, temp, offset);
// 读到数据就进行写入
if (0 < res)
{
write(dest1, temp, res);
}
// 没读到数据但成功执行读取函数则退出
else if (0 == res)
{
// 输出提示
printf("主线程中----图片复制完成\n");
break;
}
// 执行读取函数失败则返回NULL
else
{
ERR_MSG("read file error");
return -1;
}
}
// 修改偏移量
offset -= sizeof(temp);
}
// 释放两个文件IO函数
close(src1);
close(dest1);
// 等待子线程结束
pthread_join(tid, NULL);
// 输出提示
printf("图片复制完成\n");
return 0;
}
void *half_copy(void *half_offset)
{
// 检查传进来的偏移量
// printf("子线程half_offset = %ld\n", *(off_t *)half_offset);
// 偏移量,偏移一半
off_t offset = *(off_t *)half_offset;
// 打开两个文件,读源文件,写目的文件
int src2 = open("1.png", O_RDONLY);
int dest2 = open("copy.png", O_WRONLY);
// 将源文件和目的文件向后偏移一半
lseek(src2, offset, SEEK_SET);
lseek(dest2, offset, SEEK_SET);
// 用于接收读取的字节数返回值
ssize_t res;
// 创建用于过度的存储变量
char temp[256];
while (1)
{
// 复制后半部分就不用担心读取越界了
// 因为读到最后若过度存储容量没占满,那么有多少读多少
res = read(src2, temp, sizeof(temp));
// 读到数据就进行写入
if (0 < res)
{
write(dest2, temp, res);
}
// 没读到数据但成功执行读取函数则退出
else if (0 == res)
{
// 输出提示
printf("子线程中----图片复制完成\n");
break;
}
// 执行读取函数失败则返回NULL
else
{
ERR_MSG("read file error");
return NULL;
}
}
// 释放两个文件IO函数
close(src2);
close(dest2);
// 结束子线程
pthread_exit(NULL);
}
三、多线程翻转输出字符串代码
#include <my_head.h>
// 用于翻转的字符串
char str[8] = "1234567";
void *turn_str(void *flag);
int main(int argc, const char *argv[])
{
// 标志信号
int flag = 0;
// 创建子线程,子线程号为tid
pthread_t tid;
if (pthread_create(&tid, NULL, turn_str, (void *)&flag) != 0)
{
fprintf(stderr, "pthread_create error __%d__\n", __LINE__);
return -1;
}
// 暂定打印90次,太多不容易看,打太快了
int i = 0;
while (i < 90)
{
// 当标志信号为0时输出字符串,输出完再将标志信号换成1
if (flag == 0)
{
printf("%s\n", str);
i++;
// 改变标志信号
flag = 1;
}
}
return 0;
}
// 翻转
void *turn_str(void *flag)
{
int len = strlen(str);
while (1)
{
// 当标志信号为1时进行翻转,翻转后再将标志信号换成0
if (*(int *)flag == 1)
{
char *s = str, *s1 = str + len - 1;
// 翻转
while (s < s1)
{
(*s) ^= (*s1);
(*s1) ^= (*s);
(*s) ^= (*s1);
s++;
s1--;
}
// 改变标志信号
*(int *)flag = 0;
}
}
}