使用单例模式并跨线程传递数据实践

背景

程序接入一个手柄作为输入设备,手柄摇杆的位置值可能被其他任务(可能不止一个)所使用。

解决方案

由于只有其他任务只会使用手柄数据,而不会写入,对其他任务来说,手柄数据是只读的。可以使用一个任务读取手柄数据,而其他任务通过接口读取即可。手柄对象设计采用单例模式,数据采集任务中通过一个手柄对象去读取外设的数据,而其他任务中只调用读取手柄数据的接口。

具体实现

手柄类声明部分

static std::mutex joystic_mtx;

class Joystick{

 public:
  static Joystick* Getinstance() {
    if (instance_ == NULL) {
      std::lock_guard<std::mutex> guard(joystic_mtx);
      if (instance_ == NULL) {
        instance_ = new Joystick();
        static GarCollector gc;
      }
    }
    return instance_;
  }
  void operator()();
  bool isJoystickValid();
  void GetJoyXYPos(int& wXpos, int& wYpos);
  void ReadJoystick(); 

 private:
  Joystick(){};
  void TranslateJoyPos();  

  class GarCollector {
   public:
    ~GarCollector() {
      if (Joystick::instance_) {
        delete Joystick::instance_;
        Joystick::instance_ = NULL;
      }
    }
  };

  static Joystick* instance_;  

  JOYINFO joy_basic_info_;  // 定义joystick信息结构体
  MMRESULT valid_flag_;
  int xy[2] = {0, 0};
};

其中 Getinstance 函数中采用了[[使用单例模式进行多线程编程]]中所提到的双重判定写法,确保在多个线程实例化切线程的过程中不会误判断。另外,使用 GarCollector 来保障资源回收。

手柄类实现部分

Joystick *Joystick::instance_ = NULL;  

bool Joystick::isJoystickValid() {
  if (valid_flag_ == JOYERR_NOERROR) return TRUE;
  return FALSE;
}

void Joystick::GetJoyXYPos(int &Xpos, int &Ypos) {
  Xpos = xy[0];
  Ypos = xy[1];
}

void Joystick::operator()() {
  ReadJoystick();
  TranslateJoyPos();
}

void Joystick::TranslateJoyPos() {
  const int sample_limit = 65536 / 2;
  const int valid_threshold = sample_limit / 10;
  xy[0] = std::abs(sample_limit - int(joy_basic_info_.wXpos)) < valid_threshold
              ? 0
              : int(joy_basic_info_.wXpos) - sample_limit;
  xy[1] = std::abs(sample_limit - int(joy_basic_info_.wYpos)) < valid_threshold
              ? 0
              : sample_limit - int(joy_basic_info_.wYpos);
}
  
void Joystick::ReadJoystick() {
  valid_flag_ = joyGetPos(JOYSTICKID1, &joy_basic_info_);
}

多线程并行读取数据方式

void joystick_thread() {
  Joystick* joystick = Joystick::Getinstance();
  joystick->ReadJoystick();

  while (true) {
    if (joystick->isJoystickValid()) {
      (*joystick)();
      Sleep(100);
    } else {
      cout << "please make sure joystick is valid" << endl;
      Sleep(1000);
      joystick->ReadJoystick();
    }
  }
}
  
void get_joystick_thread() {
  Joystick* joystick = Joystick::Getinstance();
  int xpos, ypos;
  while (true) {
    joystick->GetJoyXYPos(xpos, ypos);
    if (joystick->isJoystickValid()) {
      cout << Format("x is {0}, y is {1}", xpos, ypos) << endl;
    }
    Sleep(500);
  }
}

  

int main() {
  Joystick* joystick = Joystick::Getinstance();
  std::thread joystick_th(joystick_thread);
  std::thread get_joystick_th(get_joystick_thread);

  joystick_th.join();
  get_joystick_th.join();
  return EXIT_SUCCESS;
}

joystick_thread 线程一直读取手柄的输入数据,并存到 joystick 类中,get_joystick_thread 作为其他任务读取 joystick 类中的数据并使用。

Reference

posted @ 2023-10-23 16:51  pomolnc  阅读(31)  评论(0编辑  收藏  举报