2011-05-07 7 views
2

async_write()은 다른 스레드에서 동시에 호출 할 수 없습니다. async_write_some을 사용하여 청크 단위로 데이터를 전송하며 이러한 청크는 인터리브 될 수 있습니다. 따라서 async_write()을 동시에 호출하지 않도록 처리하는 것은 사용자의 책임입니다.동시 async_write. 대기없는 솔루션이 있습니까?

이 의사 코드보다 좋은 해결책이 있습니까?

void send(shared_ptr<char> p) { 
    boost::mutex::scoped_lock lock(m_write_mutex); 
    async_write(p, handler); 
} 

저는 꽤 오랫동안 다른 스레드를 차단하는 아이디어가 없습니다 (내 응용 프로그램에서 ~ 50Mb 보냄).

그럴 수 있습니까?

void handler(const boost::system::error_code& e) { 
    if(!e) { 
    bool empty = lockfree_pop_front(m_queue); 
    if(!empty) { 
     shared_ptr<char> p = lockfree_queue_get_first(m_queue); 
     async_write(p, handler); 
    } 
    } 
} 

void send(shared_ptr<char> p) { 
    bool q_was_empty = lockfree_queue_push_back(m_queue, p) 
    if(q_was_empty) 
    async_write(p, handler); 
} 

바로 사용할 수있는 요리 책 요리법을 찾고 싶습니다. lock-free를 다루는 것은 쉬운 일이 아니며 많은 미묘한 버그가 나타날 수 있습니다.

+2

전송 속도가 가능한 한 빨리 실행된다고 가정하면 여러 스레드의 데이터를 인터리빙하면 어떤 이점이 있습니까? 전체 전송 속도가 빨라지지는 않을 것이고 스레드가 평균적으로 더 빨리 끝나지 않을 것입니다. –

+0

async_write()에 전달 된 전체 버퍼는 인접한 블록으로 보내야합니다. 구조가 있습니다. 헤더와 파일 내용과 함께 http-answer와 같은 것을 상상해보십시오. 동시 async_write()가 있으면 구조가 손상됩니다. – user222202

+0

부스트 문서로부터 : 이 조작은 스트림의 async_write_some 함수에 대한 0 회 이상의 호출로 구현되며 작성 조작이라고합니다. 프로그램은이 작업이 완료 될 때까지 스트림이 다른 쓰기 작업 (async_write, 스트림의 async_write_some 함수 또는 쓰기 작업을 수행하는 혼합 된 작업)을 수행하지 않도록해야합니다. – user222202

답변

3

async_write()가 다른 스레드

이 문에서 동시에 호출 으로 금지하는 것은 매우 올바르지 않습니다. 서로 다른 socket 개체에있는 한 응용 프로그램은 async_write을 동시에 호출 할 수 있습니다.

의사 코드 의사 코드가 더 좋습니까?

void send(shared_ptr<char> p) { 
    boost::mutex::scoped_lock lock(m_write_mutex); 
    async_write(p, handler); 
} 

이 가능성이 async_write 즉시 반환하기 때문에 의도가 무엇을 달성하지 않습니다. 쓰기 작업의 전체 지속 기간 동안 잠긴 뮤텍스를 사용하려는 경우 완료 핸들러가 호출 될 때까지 scoped_lock을 범위에 유지해야합니다.

이 문제에 대한 해결책이 더 있지만 라이브러리에는 strand이라는 개념을 사용하는 기본 제공 지원이 있습니다. 이 시나리오에 잘 들어 맞습니다.

가닥은 이벤트 엄격 순차 호출 핸들러 (즉 없이 동시 호출)으로 정의된다. 스트랜드를 사용하면 코드를 다중 스레드 된 프로그램에서 실행 (명시 적 잠금 (예 : 뮤텍스 사용) 필요 없음) 할 수 있습니다.

여기서 명시 적 가선을 사용하면 io_service::run()을 호출 한 단일 스레드에서만 핸들러를 호출 할 수 있습니다. 귀하의 예를 들어, m_queue 회원은 나가는 메시지 대기열에 대한 원자 적 액세스를 보장하면서 가닥에 의해 보호됩니다. 큐에 항목을 추가 한 후 크기가 1이면 처리가 진행 중이고 대기열을 통해 랩 된 응용 프로그램을 시작할 수 없음을 나타냅니다. 대기열 크기가 1보다 크면 응용 프로그램에서 async_write이 완료 될 때까지 기다려야합니다. async_write 완료 핸들러에서 대기열의 항목을 팝 아웃하고 필요한 경우 오류를 처리하십시오. 큐가 비어 있지 않으면 완료 핸들러는 큐 앞에있는 다른 async_write을 시작해야합니다.

내장 된 Asio 구문을 의도 한대로 사용하므로 클래스에 뮤텍스를 뿌리기가 훨씬 더 깔끔한 디자인입니다. 이 other answer I wrote에는이 디자인을 구현하는 몇 가지 코드가 있습니다.

+1

당신은'strand'에 대해 확신합니까? 비 인터리빙 된 패킷을 비동기 적으로 보내는 방법에 대한 의문이 있다고 생각합니다. 내가 믿는 것과 똑같은 문제가있다. 그는'io_service :: run()'을 호출하는 하나의 스레드와 어떤 응답을 쓰고 싶어하는 다른 스레드를 가지고있다. 예를 들어, 다른 2 개의 스레드가 같은'socket' 객체에서'async_write()'를 실행하면'async_write_some()'에 대한 실제 호출 순서가 정의되지 않습니다. 'strand'만이이 쓰기 작업의 완료 핸들러 만 직렬화되도록 보장합니다. 어쨌든 하나의 스레드가'io_service :: run() '을 호출했기 때문에 그렇습니다. –

+0

@DragomirIvanov 예 'async_write'에 대한 호출을 직렬화하는 데 사용되는 보내는 메시지 대기열에 대한 액세스를 보장해야합니다. 나는 명확하게 대답을 업데이 트했습니다. –

1

우리는 소켓 객체에 별도의 데이터 대기열을 기록하도록함으로써이 문제를 해결했습니다. 기록 할 첫 번째 데이터 조각이 "대기"되면 async_write()을 시작합니다. 우리의 async_write 완료 핸들러에서 전송할 데이터가 여전히 남아있는 경우 후속 async_write 작업을 시작합니다.

+0

. 샘 밀러 (Sam Miller)의 대답에 제시된대로 가닥을 사용하면 전송 대기열에 동시 액세스하지 못하게되므로 뮤텍스가 필요하지 않습니다. 따라서이 솔루션은 제대로 구현되면 요청에 따라 대기 상태가됩니다. – ComicSansMS

관련 문제