idealvin / coost

A tiny boost library in C++11.
Other
4.01k stars 563 forks source link

能支持channel进行close判定吗 #289

Closed Z-eddy closed 1 year ago

Z-eddy commented 1 year ago

库写的很棒,希望能增加类似go判定通道已经关闭这个功能: 协程0 消费者

while(chan){
  // 通道没关闭时进行数据处理
  // 通道进行close后不再执行
}

协程1 生产者

// 写入数据
chan<<xxx;
// 关闭通道,关闭后所有执行while(chan)退出while循环
close(chan)
idealvin commented 1 year ago

@Z-eddy

master 代码已经支持所述功能,可以试试

由于增加了关闭状态,以及支持在协程、线程中混用channel,不能再用 co::timeout() 判断读写操作是否超时。

co::chan<fastring> ch(8, 32);
ch << fastring("hello");
if (ch.done()) {
    COUT << "write to channel ok..";
}

fastring s;
ch >> s;
if (ch.done()) {
    COUT << "read from channel ok, s: " << s;
}

ch.close();
if (!ch) {
    COUT << "channel was closed..";
}
Z-eddy commented 1 year ago

已经尝试了下,存在这个问题: 使用ch.close()后会立即关闭通道,此时ch中还存在最后一个数据,使用ch.done()进行判断,因为通道关闭,无法读取到最后一个数据。测试代码如下:

co::Chan<std::string> ch;

// 写入数据
  go([=] {
    for (int i{0}; i != 10; ++i) {
      std::string tempVal{};
      tempVal = "current:" + std::to_string(i);
      ch << tempVal;
    }
    ch.close();
  });

// 读取数据

  go([=] {
    std::string val{};
    while (ch.done()) {
      ch >> val;
      std::cout << val << std::endl;
    }
    std::cout << "done";
  });

输出结果: current:0 current:1 current:2 current:3 current:4 current:5 current:6 current:7 current:8 done

idealvin commented 1 year ago

需求不明确,channel close 后 可以读、不能写?

idealvin commented 1 year ago

chan::done() 用于判断读写操作是否正常完成,判断channel是否关闭直接用 while(ch)

Z-eddy commented 1 year ago

这里写入通道的数据i是0~9

for (int i{0}; i != 10; ++i) {
      std::string tempVal{};
      tempVal = "current:" + std::to_string(i);
      ch << tempVal;
    }
    ch.close();

读取这里只能收到0~8

while (ch.done()) {
      ch >> val;
      std::cout << val << std::endl;
    }

最后的9无法收到,数据丢失了1个

idealvin commented 1 year ago

重新调整了设计方案,chan close 后可以读,但不能写。

done() 需要在读、写操作后调用,判断读、写是否正常完成。

co::chan<std::string> ch;

// 写入数据
  go([=] {
    for (int i{0}; i != 10; ++i) {
      ch << ("current:" + std::to_string(i));
    }
    ch.close();
  });

// 读取数据

  go([=] {
    std::string val{};
    do {
      ch >> val;
      std::cout << val << std::endl;
    } while (ch.done()) 
    std::cout << "done";
  });
Z-eddy commented 1 year ago

测试了下你给的这个demo,最后一个数据9会输出两次,且如果在写入数据那直接调用close,会收到一个空字符串:

co::chan<std::string> ch;

// 写入数据
  go([=] {
    // 直接调用关闭
    ch.close();
  });

// 读取数据
  go([=] {
    std::string val{};
    do {
      ch >> val;
      std::cout << val << std::endl;
    } while (ch.done());
    std::cout << "done";
  });

按照使用习惯,可能这样的读取数据写法会更方便点(这种写法不会输出最后的9):

// 读取数据
  go([=] {
    std::string val{};
    while (ch) {
      ch >> val;
      std::cout << val << std::endl;
    }
    std::cout << "done";
  });

就像go中这样的用法:

func readData(ch chan int) {
  for num := range ch {
    fmt.Println(num)
  }
}

func writeData(ch chan int) {
  for i := 0; i < 10; i++ {
    ch <- i
  }
  close(ch)
}

func main() {
  ch := make(chan int)
  go writeData(ch)
  readData(ch)
}
idealvin commented 1 year ago

@Z-eddy 并没有重复,只是最后一次读失败,未加判断

co::chan<std::string> ch;

// 写入数据
  go([=] {
    for (int i{0}; i != 10; ++i) {
      ch << ("current:" + std::to_string(i));
    }
    ch.close();
  });

// 读取数据

  go([=] {
    std::string val{};
    do {
      ch >> val;
      if (ch.done()) std::cout << val << std::endl;
    } while (ch.done()) 
    std::cout << "done";
  });
Z-eddy commented 1 year ago

加上判断后没问题了,你们好效率啊!