Function Pointer trong C++

Mình đang vọc cách truyền function pointer vào một thread.
Mình có 2 cái function pointer, 1 cái không có giá trị truyền vào, cái kia có.
Cái phần dưới đây chạy bình thường. printApple là một function in ra màn hình một chữ Apple thôi.

typedef void(*Apple)();
Apple fruit = printApple;
std::thread appleThread(fruit);
appleThread.join();

Cái dưới này lại không chạy được. printOrange(int price) là một function có 1 tham số truyền vào (giá) và in ra màn hình một câu “Giá Orange: “ kèm theo tham số truyền vào.

typedef void(*Orange)(int price);
Orange juice = printOrange;
std::thread orangeThread(juice(5));
orangeThread.join();

Cái orangeThread nó ghi là “no instance constructor “std:: thread::thread” matches the argument list. argument types are: (void).
Cho mình hỏi tại sao lại như vậy? Khác nhau ở chỗ một cái có tham số, cái kia không thôi mà.

ko truyền kiểu đó được vì juice(5) là gọi hàm juice rồi, trả về kiểu của Orangevoid :V

nhòm signature của nó nè:
https://en.cppreference.com/w/cpp/thread/thread/thread
image

truyền function f, sau đó là truyền các arguments của nó. Vậy viết lại là

std::thread orangeThread(juice, 5);

gọi nhiều tham số thì cứ truyền nhiều tham số vào:

std::thread orangeThread(juice, 5, "hello", 5.5f);

nếu hàm truyền vào có arg là reference, ví dụ int(int&) thì xài std::ref để truyền tham trị, nếu là const reference thì xài std::cref

std::thread orangeThread(f, std::ref(a)); //sẽ gọi f(a) với a là int&
std::thread orangeThread(f, std::cref(a)); //sẽ gọi f(a) với a là const int&

Function là 1 template nên có thể truyền lambda, function object, std::function vào, tiện hơn function pointer :V

std::string str = "hello";
float x = 0.1f;
std::thread t([](int n, const char* s, double f, const std::string& str, float& x){
    /* ... */ 
}, 5, "hi", 1.3, std::cref(str), std::ref(x));

// hoặc

auto f = [](int n, const char* s, double f, const std::string& str, float& x){
    /* ... */ 
};
std::thread t(f, ...);
5 Likes

Tiện đây cho mình hỏi luôn là khi truyền con trỏ (pointer) vào một thread thì nên làm như thế nào? Theo mình biết thì nếu mình không nói là truyền reference thì nó sẽ copy và truyền bản sao đó vào thread. Nhưng pointer thì lưu 1 địa chỉ nên mình đoán là truyền như thế nào cũng được.
Ví dụ:

int apple = 10;
int* pointer = &a;
void printValue(int* pointer)
{
std::cout<< *pointer << std::endl;
}

std::thread thread_1(printValue, pointer);
thread_1.join();

std::thread thread_2(printValue, pointer);
thread_2.join();

truyền vậy đúng rồi

mà truyền con trỏ chỉ để thay đổi giá trị của biến mà nó trỏ tới thì truyền thẳng reference luôn cho rồi :V

truyền apple bằng reference ấy, ko phải truyền con trỏ bằng reference :V :V

int apple = 10;

void printValue(int& n)
{
    std::cout<< n << std::endl;
}

std::thread thread_1(printValue, std::ref(apple));
thread_1.join();

std::thread thread_2(printValue, std::ref(apple));
thread_2.join();

2 thread xài chung 1 biến thì coi chừng race codition nha :V

printValue xài chung std::cout vậy cũng có race codition :V, thêm cái lock_guard mutex vào:

// global 
std::mutex cout_mutex;

void printValue(int& n)
{
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout<< n << std::endl;
}

nếu ko muốn cái mutex kia ở global namespace thì có thể cho nó vào namespace nào đó, hoặc biến nó thành biến static của 1 class nào đó, hoặc biến nó thành biến static của 1 hàm nào đó giống như singleton :V

hoặc quẳng nó vào trong printValue luôn :V với điều kiện printValue ko được phép là template function :V

void printValue(int& n)
{
    static std::mutex mut; // `mut` vừa guard `cout` vừa guard `n`
    std::lock_guard<std::mutex> lock(mut);
    std::cout<< ++n << std::endl;
}
4 Likes

Cám ơn bạn. Câu trả lời rất chi tiết

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?