8

私はC++11 std::threadを使用しています。そのメインループは、DATAGRAMソケットに到着するUDPパケットをリッスンするブロッキングrecvfrom()呼び出しと、メッセージを解析してプロセス内のSTLコンテナーのロードを操作する高度なコードで構成されています。

スレッドはクラス()に属しhelloexchange、コンストラクタによって開始され、デストラクタでキャンセルする必要があります。明らかな理由で、スレッドを強制的に終了したくありません。これは、クラスの外部に部分的に配置されているデータ構造を破壊する可能性があるためです。

pthreadの代わりにを使用する場合、必要なすべての機能を提供するメソッドがありstd::threadます。特定のシステムコールでブロックしている間のみスレッドをキャンセルし、特定のセクションではキャンセルを完全に無効にすることができます。キャンセルは、再度有効になると実行されます。これは、動作するコードの完全な例です。pthread_cancelpthread_setcancelstatepthread

#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <cstdio>
#include <pthread.h>
#include <net/if.h>
#include <ifaddrs.h>

int sock;

void *tfun(void *arg) {
    std::cout << "Thread running" << std::endl;
    while(true) {
        char buf[256];
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        socklen_t addrlen = sizeof(addr);
        //allow cancelling the thread
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

        //perform the blocking recvfrom syscall
        int size = recvfrom(sock, (void *) buf, sizeof(buf), 0,
            (struct sockaddr *) &addr, &addrlen);

        //disallow cancelling the thread
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

        if(size < 0) {
            perror("Could not receive packet");
            return NULL;
        } else {
            //process the packet in the most complex ways
            //you could imagine.
            std::cout << "Packet received: " << size << " bytes";
            std::cout << std::endl;
        }
    }
    return NULL;
}


int main() {
    //open datagram socket
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0) {
        perror("Could not open socket");
        return 1;
    }
    //bind socket to port
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1337);
    addr.sin_addr.s_addr = 0;
    if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        perror("Could not bind datagram socket");
        return 2;
    }
    //create the listener thread
    pthread_t t;
    if(pthread_create(&t, NULL, tfun, NULL) != 0) {
        perror("Could not thread");
        return 3;
    };
    //wait
    std::cin.get();
    //cancel the listener thread. pthread_cancel does not block.
    std::cout << "Cancelling thread" << std::endl;
    if(pthread_cancel(t) != 0) {
        perror("Could not cancel thread");
    }
    //join (blocks until the thread has actually cancelled).
    std::cout << "Joining thread" << std::endl;
    if(pthread_join(t, NULL) != 0) {
        perror("Could not join thread");
    } else {
        std::cout << "Join successful" << std::endl;
    }
    //close socket
    if(close(sock) != 0) {
        perror("Could not close socket");
    };
}

ただし、std::threadはサポートしていません。また、サポートもcancelしていません(リファレンスはここにあります)。ただし、内部で使用されるIDを返すはサポートします。ただし、pthread_cancel()をスレッドのネイティブハンドルに送信するだけの明らかなアプローチでは、セグメンテーション違反が発生します。std::this_threadsetcancelstatenative_handlepthread_t

#include <iostream>
#include <thread>
#include <cstdio>

#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <pthread.h>
#include <net/if.h>
#include <ifaddrs.h>

int sock;

void tfun() {
    std::cout << "Thread running" << std::endl;
    while(true) {
        char buf[256];
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        socklen_t addrlen = sizeof(addr);

        //perform the blocking recvfrom syscall
        int size = recvfrom(sock, (void *) buf, sizeof(buf), 0,
            (struct sockaddr *) &addr, &addrlen);

        if(size < 0) {
            perror("Could not receive packet");
            return;
        } else {
            //process the packet in the most complex ways
            //you could imagine.
            std::cout << "Packet received: " << size << " bytes";
            std::cout << std::endl;
        }
    }
    return;
}

int main() {
    //open datagram socket
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0) {
        perror("Could not open socket");
        return 1;
    }
    //bind socket to port
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1337);
    addr.sin_addr.s_addr = 0;
    if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        perror("Could not bind datagram socket");
        return 2;
    }
    //the listener thread
    std::thread *t = new std::thread(&tfun);
    //wait
    std::cin.get();
    //cancel the listener thread. pthread_cancel does not block.
    std::cout << "Cancelling thread" << std::endl;
    if(pthread_cancel(t->native_handle()) != 0) {
        perror("Could not cancel thread");
    }
    //join (blocks until the thread has actually cancelled).
    std::cout << "Joining thread" << std::endl;
    t->join();
    delete t;
    //close socket
    if(close(sock) != 0) {
        perror("Could not close socket");
    };
}

結果:

(gdb) run
Starting program: /tmp/test/test-dbg 
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7ffff6550700 (LWP 11329)]
Thread running

Cancelling thread
Joining thread

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff6550700 (LWP 11329)]
0x00007ffff6e67b45 in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/libstdc++.so.6

std::threadシステムコールでブロックしている間キャンセルする方法はありますか?

編集

私はクロスプラットフォームソリューションを求めていません。POSIX準拠のソリューションで十分です。

4

2 に答える 2

4

ブロックを解除し、厄介なスレッドキャンセルビジネス全体を回避するための、よく知られたセルフパイプトリックに沿った回避策を提案します。select(2)

スレッドのIPアドレスとポートsocket(7)がブロックされてsendto(2)いることがわかっているので、ループから抜け出す時間であることを示す、メインスレッドからのIPアドレスへのよく知られたセンチネルパケットだけです。

このようにして、std::thread抽象化を覆す必要がなく、適度に移植性を保つことができます。

編集0:

回避策が気に入らない場合は、テクニックと呼んでください:)

于 2012-09-09T16:25:15.267 に答える
3

スレッドにシグナル(SIGUSR1など)を送信することで、recvfromを中断できます。Linuxでは、これを機能させるには、libcの自動再起動動作を無効にする必要があります(詳細については、マルチスレッド環境でrecv()がシグナルによって中断されないことを参照してください)。

于 2012-09-10T14:02:59.720 に答える