1.3 스레드 모델
핵심 의미
1. 동기 호출
Connect(),Read(),MoveJoint()등의 인터페이스는 호출자 관점에서 모두 동기적으로 반환됩니다.- 호출 스레드는 요청 완료를 기다린 뒤
STATUS_CODE또는 조회 결과를 받습니다. - 같은
Arm인스턴스에서 동시에 시작한 네트워크 호출은 내부에서 제출 순서대로 직렬 실행됩니다.
2. 주기 작업
ControllerInfo::AcquireAccess()는2000mskeep-alive 타이머 작업을 등록합니다.JoggingControl::ContinuousMove()/MultiMove()는50ms연속 조그 타이머 작업을 등록합니다.- 이러한 주기 작업은
Arm세션의 전용 네트워크 스레드를 재사용합니다. Arm세션을 중지하면 관련 타이머 작업도 함께 정리됩니다.
3. 구독/발행(SubPub)
TopicPubSub의 수신 패킷은 WebSocket 라이브러리 콜백을 통해 SDK로 들어옵니다. 이 페이지에서는 WebSocket 라이브러리 자체 스레드는 다루지 않습니다.- SDK는
SubPubService내부에messages메시지 큐를 유지하며,Receive()는 큐에서 다음 메시지를 동기적으로 가져옵니다. - 메시지 큐의 상한은
100개이며, 가득 차면 가장 오래된 메시지를 버립니다. - 여러
Arm/TopicPubSub인스턴스는 각자의 WebSocket 연결을 동시에 만들 수 있습니다. 그중 하나를 파괴하거나 연결 해제해도 계속 살아 있는 다른 인스턴스에는 영향을 주지 않아야 합니다.
SDK 스레드 뷰
스레드 안전 규칙
Arm 인스턴스
- 단일 스레드 접근: 각
Arm인스턴스는 하나의 업무 스레드에서 접근해야 합니다. - 다중 인스턴스 동시성: 여러 로봇을 동시에 제어해야 하는 경우 여러
Arm인스턴스를 생성합니다. - 스레드 안전하지 않음: 외부 동기화 메커니즘 없이
Arm인스턴스를 여러 스레드에서 공유하지 마십시오.
서비스 모듈
모든 서비스 모듈( motionControl , controllerInfo , alarmClient 등)은 소속된 Arm 인스턴스의 스레드 안전 특성을 따릅니다.
cpp
// 안전: 모든 모듈을 단일 스레드에서 접근합니다. // 같은 업무 스레드에서 같은 Arm을 직렬 호출합니다.
Arm arm; // Arm 세션 객체를 생성합니다.
arm.Connect("192.168.110.2", ""); // 현재 업무 스레드에서 로봇에 연결합니다.
arm.motionControl.GetCurrentPose(PoseType::JOINT); // 현재 관절 포즈를 직렬로 읽습니다.
arm.controllerInfo.GetCtrlStatus(); // 제어 상태를 직렬로 읽습니다.
arm.alarmClient.GetAllActiveAlarms(); // 활성 알람 목록을 직렬로 읽습니다.
// 안전하지 않음: 여러 스레드가 같은 Arm에 접근합니다. // 아래 두 줄은 반례입니다.
// 스레드 1 // 첫 번째 스레드가 모션을 시작하려고 합니다.
arm.motionControl.MoveJoint(target1, 0.5, 0.5); // 스레드 1이 같은 Arm의 모션 인터페이스를 호출합니다.
// 스레드 2(동시 실행) // 두 번째 스레드가 동시에 모션을 시작합니다.
arm.motionControl.MoveJoint(target2, 0.5, 0.5); // 정의되지 않은 동작입니다.안전한 동시 접근 패턴
cpp
// 안전: 여러 Arm 인스턴스로 동시 접근을 구현합니다. // 각 로봇은 독립적인 Arm 세션을 사용합니다.
Arm arm1, arm2; // 서로 독립적인 Arm 객체 두 개를 생성합니다.
arm1.Connect("192.168.110.2", ""); // 첫 번째 로봇에 연결합니다.
arm2.Connect("192.168.110.3", ""); // 두 번째 로봇에 연결합니다.
// 스레드 1 // 첫 번째 스레드는 arm1만 사용합니다.
arm1.motion.MoveJoint(target1, 0.5, 0.5); // 첫 번째 로봇에 모션을 전달합니다.
// 스레드 2(동시 실행, 다른 로봇) // 두 번째 스레드는 arm2만 사용합니다.
arm2.motion.MoveJoint(target2, 0.5, 0.5); // 안전합니다.콜백 사용 지침
권장 사항
- 콜백 함수는 짧고 빠르게 유지합니다.
- 후속 처리가 필요하면 먼저 데이터를 복사합니다.
- 스레드 안전 로그 기록을 사용합니다.
- 다른 알림이 계속 처리될 수 있도록 빠르게 반환합니다.
금지 사항
- 콜백 안에서 블로킹하지 마십시오.
- 콜백 안에서 블로킹 SDK API를 호출하지 마십시오.
- 동기화 메커니즘 없이 콜백 안에서 다른
Arm인스턴스에 접근하지 마십시오. - 콜백 안에서 예외를 던지지 마십시오.
예: 올바른 콜백 패턴
cpp
// 올바름: 빠르고 블로킹하지 않는 콜백 // 콜백 스레드는 데이터 전달만 수행합니다.
void OnJointPosition(const JointPositionMsg& msg) { // 관절 위치 메시지를 받으면 콜백에 진입합니다.
std::lock_guard<std::mutex> lock(dataMutex_); // 공유 캐시 보호를 위해 잠급니다.
latestJoints_ = msg.joints; // 메시지 데이터를 빠르게 복사합니다.
} // 즉시 반환해 수신 스레드가 막히지 않게 합니다.
void ProcessData() { // 업무 스레드에서 캐시된 데이터를 처리합니다.
std::vector<Float64> joints; // 업무 스레드 자체 데이터 사본을 준비합니다.
{ // 잠금 범위를 줄입니다.
std::lock_guard<std::mutex> lock(dataMutex_); // 공유 캐시 읽기를 위해 잠급니다.
joints = latestJoints_; // 최신 관절 데이터를 로컬 변수로 복사합니다.
} // 잠금을 해제해 콜백 스레드가 계속 캐시를 업데이트할 수 있게 합니다.
// 데이터를 안전하게 처리합니다. // 시간이 걸리는 업무 로직은 잠금 밖에서 실행합니다.
} // 업무 스레드 처리 함수를 종료합니다.예: 잘못된 콜백 패턴
cpp
// 잘못됨: 콜백 안에서 블로킹합니다.
void OnAlarm(const AlarmMsg& msg) { // 알람 메시지를 받으면 콜백에 진입합니다.
arm.alarmClient.Reset(); // 콜백 안에서 블로킹 SDK API를 호출하면 안 됩니다. 데드락 위험이 있습니다.
ProcessLargeData(msg); // 콜백 안에서 시간이 걸리는 처리를 수행하면 안 됩니다. 다른 콜백을 막습니다.
} // 잘못된 예제 콜백을 종료합니다.권장 사용 방식
- 같은
Arm의Connect()/Disconnect()/ 모드 전환은 동일한 업무 스레드 또는 동일한 스케줄링 로직에서 통합 관리합니다. TopicPubSub콜백에서는 핵심 필드 파싱, 큐에 전달, 플래그 설정 같은 가벼운 작업만 수행합니다.- 시간이 걸리는 로직, 2차 제어 명령, 재연결 동작은 업무 스레드에서 실행하고 콜백 안에서 직접 막지 마십시오.
사용 제약
금지되는 재진입 작업
TopicPubSub 콜백 안에서 다음 항목을 직접 호출하지 마십시오.
Arm::Connect()、Arm::Disconnect()TopicPubSub::Connect()、TopicPubSub::Disconnect()StartReceiving()、RemoveMessageHandler()
이러한 재진입 작업은 OTHER_ERR 를 반환하거나 무시될 수 있습니다.
기타 제약
- 서로 다른 인터페이스 모듈이 같은 로봇에 병렬로 요청한다고 가정하지 마십시오. 같은
Arm의 요청은 기본적으로 직렬 스케줄링된다고 이해하는 것이 더 안전합니다. - 업무에서 높은 처리량이나 장치 격리가 필요하면 여러
Arm/ArmHandle*세션을 사용하십시오.
C99 설명
Arm_Destroy()전에 먼저Arm_Disconnect()를 호출해 해당 세션의 네트워크 스레드와 타이머 작업이 정상적으로 정리되도록 해야 합니다.
성능 고려 사항
연결 복구
네트워크 스레드가 연결 끊김을 감지한 경우:
- 연결 복구는 호출자가
Disconnect()를 호출한 뒤 다시Connect()를 호출해 수행합니다. - 모든 대기 중인 작업은 오류 코드를 반환합니다.
모범 사례 요약
- 데이터 복사: 콜백에서 데이터를 복사하고 작업 스레드에서 처리합니다.
- 오류 처리: 항상 반환 코드를 확인하고 연결 끊김을 적절히 처리합니다.
- 리소스 정리:
Arm인스턴스를 파괴하기 전에 반드시Disconnect()를 호출합니다.