只有一个读线程,一个写线程,使用read_idx和write_idx, 是否可以代替锁?

registercsdn 2018-11-22 09:15:46
申请一块内存,分10小块, 循环使用
写线程先写,之后读线程读取数据,使用锁可以很容易的完成互斥。

假如我不使用锁,完全使用int read_idx=0, int write_idx=0, 似乎可以完全代替锁,请大家比较一下这种方式和锁的优缺点?
下面是使用read_idx, write_idx的伪代码。

write_thread(){
while(1){
if(write_idx!=read_idx){
memory[write_idx] = random_value;
}else
continue;

if((write_idx +1) == 10)
write_idx = 0;
else
write_idx++;
}
}

read_thread(){
while(1){
if(read_idx!=read_idx){
printf(memory[read_idx]);
}else
continue;

if((read_idx +1) == 10)
read_idx = 0;
else
read_idx++;
}
}
...全文
435 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
sdghchj 2020-05-02
  • 打赏
  • 举报
回复
如果你说的10小块中所谓"小块"是指一个字长以内的类型,那是不需要加锁,线程安全的,因为CPU一次处理的数据就是以字长为单位,其读写是原子性的。但它是不是如你所想的逻辑那样运行那就不好说了。比如语法跟语义,语法上正确,语义上不见得正确。 其它情况还得加锁。
qybao 2020-05-01
  • 打赏
  • 举报
回复
或许加个state(状态)控制一下可能会好一些
当然,如果要保证读写一致,还是需要锁的,这里只是说明互不干涉,不等价于同步。
比如

volatile int running = 1, writing = 1, reading = 1;
int write_idx = 0, read_idx = 0, wcnt = 0, rcnt = 0;

class buff {
public:
int state;
char *ptr;
buff(int bufsize) {
state = 0;
ptr = new char[bufsize];
}
~buff() {
delete []ptr;
}
};
buff *memory[10] = {
new buff(255),
new buff(255),
new buff(255),
new buff(255),
new buff(255),
new buff(255),
new buff(255),
new buff(255),
new buff(255),
new buff(255)
};

void write_thread(){
while(running){
while(1) {
if (memory[write_idx]->state==0) break; //找到可写
write_idx = (write_idx+1)%10;
}
wcnt++;
struct timespec tn;
clock_gettime(CLOCK_REALTIME, &tn);
sprintf(memory[write_idx]->ptr, "write-%d:%ld", write_idx, tn.tv_nsec);
printf("%s\n", memory[write_idx]->ptr);
memory[write_idx]->state = 1; //写完修改为可读
write_idx = (write_idx+1)%10;
}
writing = 0;
}

void read_thread(){
while(running){
while(1) {
if (memory[read_idx]->state==1) break; //找到可读
read_idx = (read_idx+1)%10;
}
rcnt++;
printf("read-%d:%s\n", read_idx, memory[read_idx]->ptr);
memory[read_idx]->state = 0; //读完后修改为可写
read_idx = (read_idx+1)%10;
}
reading = 0;
}

int main() {
thread* t1 = new std::thread(&write_thread);
thread* t2 = new std::thread(&read_thread);
this_thread::sleep_for(chrono::milliseconds(10));
running = 0;
while (writing);
while (reading);
for (int i=0; i<10; i++) {
delete memory[i];
}
printf("write_time=%d, read_time=%d\n", wcnt, rcnt);
return 0;
}

qybao 2020-05-01
  • 打赏
  • 举报
回复
引用 23 楼 registercsdn 的回复:
只需要把write_idx, read_idx定义成atomic就可以了吧。也不需要用锁。

最初的代码,有个地方打错了,在读线程中,判断语句是
    if(read_idx!=write_idx){


因为帖子不提供编辑功能,特此更正。

atomic_int read_idx = 0;
atomic_int write_id = 0;
假设write早一步启动,write_idx先变成1, 启动的时候不会死锁。
write_thread(){
while(1){
if(write_idx!=read_idx){
memory[write_idx] = random_value;
}else
continue;

if((write_idx +1) == 10)
write_idx = 0;
else
write_idx++;
}
}

read_thread(){
while(1){
if(read_idx!=write_idx){
printf(memory[read_idx]);
}else
continue;

if((read_idx +1) == 10)
read_idx = 0;
else
read_idx++;
}
}


引用 9 楼 cocoabird 的回复:
你这种方法和锁的道理是一样的,只是有这样的可能,你写的时候刚判断index后,读的线程也在运行,就改变了index的值

你这个只能说两个线程互不干扰(不同的线程修改不同的变量,不发生冲突),并不能说和锁等价。
但是你这样又个隐患,就是如果read_idx==write_idx 就会死循环,就是一直走你的continue分支,最终两个index都没能修改


registercsdn 2020-05-01
  • 打赏
  • 举报
回复
只需要把write_idx, read_idx定义成atomic就可以了吧。也不需要用锁。

最初的代码,有个地方打错了,在读线程中,判断语句是
    if(read_idx!=write_idx){


因为帖子不提供编辑功能,特此更正。

atomic_int read_idx = 0;
atomic_int write_id = 0;
假设write早一步启动,write_idx先变成1, 启动的时候不会死锁。
write_thread(){
while(1){
if(write_idx!=read_idx){
memory[write_idx] = random_value;
}else
continue;

if((write_idx +1) == 10)
write_idx = 0;
else
write_idx++;
}
}

read_thread(){
while(1){
if(read_idx!=write_idx){
printf(memory[read_idx]);
}else
continue;

if((read_idx +1) == 10)
read_idx = 0;
else
read_idx++;
}
}


引用 9 楼 cocoabird 的回复:
你这种方法和锁的道理是一样的,只是有这样的可能,你写的时候刚判断index后,读的线程也在运行,就改变了index的值
yshuise 2020-04-22
  • 打赏
  • 举报
回复
写指针写到一半被读指针读了,所以wrong!
ddoge 2020-04-22
  • 打赏
  • 举报
回复
我看了半天,这些人都是没读完代码的吗?一个线程控制写指针,一个线程控制读指针,为啥会有冲突?都是一句不可以,不是原子操作,你倒是说说为啥不可以啊?
fohoo 2018-12-18
  • 打赏
  • 举报
回复
不可以,你考虑问题不够细,好好看看你的代码,会有冲突
zgbzsu2008 2018-12-18
  • 打赏
  • 举报
回复
参考下c++11 原子变量 自旋锁的实现
registercsdn 2018-12-15
  • 打赏
  • 举报
回复
引用 9 楼 cocoabird 的回复:
你这种方法和锁的道理是一样的,只是有这样的可能,你写的时候刚判断index后,读的线程也在运行,就改变了index的值


写的时候判断完index后,读线程就算改变了index值,也不会影响啊,因为读的时候还会继续判断是否和写index是否相等,才会去读取数据。您可以举个例子说一下上面的程序index问题出现在何处?谢谢
registercsdn 2018-12-15
  • 打赏
  • 举报
回复
只要判断的时候没有问题,其他时候改,不会受到影响,判断语句半截应该不会被中断。请举例说明问题出在什么地方,谢谢。

引用 5 楼 wallesyoyo 的回复:
你在判断值之后,在下一步代码执行之前,你判断的值就有可能在另一个线程被修改了。
registercsdn 2018-12-15
  • 打赏
  • 举报
回复
假如读线程有操作,导致两个index相等了,另外读线程不会对内存进行读取,所以感觉没有问题。

引用 4 楼 英雄@末路 的回复:
加锁吧,一个简单的例子,这段代码

if(write_idx!=read_idx){
memory[write_idx] = random_value;
}else

判断时候连个index不等,但判断完,赋值之前,另外一个线程有操作,导致连个index相等了,那后面还会你想要的结果么?判断到赋值并不是原子操作
赵4老师 2018-12-15
  • 打赏
  • 举报
回复
不可以
mirro 2018-11-26
  • 打赏
  • 举报
回复
不加锁,会内存访问冲突的
ztenv 版主 2018-11-24
  • 打赏
  • 举报
回复
可以这么说,没有优点吧
wallesyoyo 2018-11-22
  • 打赏
  • 举报
回复
你在判断值之后,在下一步代码执行之前,你判断的值就有可能在另一个线程被修改了。
英雄@末路 2018-11-22
  • 打赏
  • 举报
回复
加锁吧,一个简单的例子,这段代码

if(write_idx!=read_idx){
memory[write_idx] = random_value;
}else

判断时候连个index不等,但判断完,赋值之前,另外一个线程有操作,导致连个index相等了,那后面还会你想要的结果么?判断到赋值并不是原子操作
自信男孩 2018-11-22
  • 打赏
  • 举报
回复
另外,这句代码是不是有问题呢?
if(read_idx!=read_idx){
自信男孩 2018-11-22
  • 打赏
  • 举报
回复
就要看这两个线程是否会共享数据,从你的例程上看,不能代替锁。
还是建议加上锁
jiht594 2018-11-22
  • 打赏
  • 举报
回复
老老实实加锁
ych607 2018-11-22
  • 打赏
  • 举报
回复
加锁才是正道吧
加载更多回复(6)

64,649

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧