コンテンツにスキップ

Top

GTK3+ で スレッド処理

ボタン押して長く時間がかかる処理とかをするとスレッド使わないと固まってしまう。
ので、スレッドを使いたいけどいまいちやり方がわからなかったので調べた。

以下は、ボタンを押したらラベルの数字がカウントアップするサンプル。

ただし、ボタンを連続で押したりとかそのあたりの排他とか全然いれてないので、実際使う場合は排他に留意しないといけない。

ソースコード(thread_sample.cpp)

#include <gtkmm.h>
#include <string>
#include <mutex>
#include <thread>

class ThreadSample : public Gtk::Window
{
public:
    ThreadSample(int width, int height);
    virtual ~ThreadSample() = default;

    sigc::connection m_conn;
    bool update();
    void cb_count_up();
    void count_up_th();

private:
    static std::mutex m_mtx;
    int m_count;
    Gtk::Label  m_label;
    Gtk::Button m_button;
    Gtk::Fixed  m_fixed;
};

// カウンターの排他フラグ
std::mutex ThreadSample::m_mtx;

// コンストラクタ
ThreadSample::ThreadSample(int width, int height)
{
    m_count = 0;
    m_conn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &ThreadSample::update), 100);

    set_title("カウントアップサンプル");
    set_default_size(width, height);

    m_label.set_text("0");
    m_label.set_size_request(320, 120);
    m_label.show();

    m_button.set_label("カウントアップ");
    m_button.set_size_request(300, 100);
    m_button.signal_clicked().connect(sigc::mem_fun(*this, &ThreadSample::cb_count_up));
    m_button.show();

    m_fixed.put(m_label,    0,    0);
    m_fixed.put(m_button,  10,  130);
    m_fixed.show();

    add(m_fixed);
}

// 100msecごとに呼び出されるようにしている関数
bool ThreadSample::update()
{
    // ラベルを更新
    {
        std::lock_guard<std::mutex> lock(ThreadSample::m_mtx);
        m_label.set_text((std::to_string(m_count)).c_str());
        if (m_count == 10){
            m_count = 0;
            m_button.set_sensitive(true);
        }
    }
    return true;
}

// カウンターを更新するスレッド関数
void ThreadSample::count_up_th()
{
    for(int i = 0; i < 10; i++)
    {
        {
            std::lock_guard<std::mutex> lock(ThreadSample::m_mtx);
            m_count++;
        }
        sleep(1);
    }
    // m_button.set_sensitive(true); // 失敗。スレッドのなかでm_button更新したら落ちた
}

// ボタンが押されたら呼び出される関数
void ThreadSample::cb_count_up()
{
    m_button.set_sensitive(false);
    std::thread th(&ThreadSample::count_up_th, this);
    th.detach(); // joinせずに投げっぱなし
}

int main(int argc, char* argv[])
{
    auto app = Gtk::Application::create(argc, argv, "work.jitaku.gtkmm.sample");
    ThreadSample sample_window(320, 240);
    return app->run(sample_window);
}

コンパイル

g++ thread_sample.cpp  `pkg-config --cflags --libs gtkmm-3.0` -o thread_sample

実行結果
タイトルタイトル

以上!

スレッド内でGUI触ったら落ちた

m_buttonのset_sensitiveをスレッドの中から呼び出したら落ちた。GTKはスレッドから呼び出したらだめみたい。

以上!