c ++ Döngü yazdırma yanlış değerleri için içerideki dişler


19

C ++ Multi-threading anlamaya çalışıyorum, ama ben bu sorun sıkışmış: Eğer bir for döngüsünde iş parçacıkları başlatmak yanlış değerler yazdırın. Bu kod:

#include <iostream>
#include <list>
#include <thread>

void print_id(int id){
    printf("Hello from thread %d\n", id);
}

int main() {
    int n=5;
    std::list<std::thread> threads={};
    for(int i=0; i<n; i++ ){
        threads.emplace_back(std::thread([&](){ print_id(i); }));
    }
    for(auto& t: threads){
        t.join();
    }
    return 0;
}

0,1,2,3,4 değerlerini yazdırmayı bekliyordum ama çoğu kez aynı değeri iki kez aldım. Bu çıktı:

Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5

Ne eksik?


7
Değere igöre lambda'ya geç [i].
rafix07

1
Kullanımınızın emplace_backtuhaf olduğunu belirtmek gerekir : emplace_backargümanların bir listesini alır ve bunu bir kurucuya iletir std::thread. Bir (rvalue) örneğini geçtiniz std::thread, bu nedenle bir iş parçacığı oluşturacak ve daha sonra bu iş parçacığını vektör içine taşıyacaksınız. Bu işlem daha yaygın yöntemle daha iyi ifade edilir push_back. Biraz daha deyimsel olan yazmak threads.emplace_back([i](){ print_id(i); });(yerinde inşa etmek) veya threads.push_back(std::thread([i](){ print_id(i); }));(inşa etmek + taşımak) daha mantıklı olurdu .
Milo Brandt

Yanıtlar:


17

[&]Sözdizimi neden olan iyakalanacak referans ile . Bu nedenle i, iş parçacığı çalıştığında beklediğinizden çok daha fazla gelişmiş olacaktır. Daha ciddisi, bir iş parçacığı çalışmadan önce kapsam dışına çıkarsa kodunuzun davranışı tanımsızdıri .

Değere igöre yakalamak - yani std::thread([i](){ print_id(i); })düzeltmedir.


2
Veya daha az kullanılmış ve genellikle tavsiye edilmezstd::thread([=](){ print_id(i); })
Wander3r

3
Bu davranış zaten tanımlanmamıştır, çünkü bu, iana iş parçacığı yazımı ve diğer iş parçacıkları okumasıyla (atomik olmayan) bir veri yarışıdır .
ceviz

6

İki sorun:

  1. İş parçacığı çalıştığında herhangi bir denetime sahip ideğilsiniz, yani lambda'daki değişkenin değeri beklediğiniz gibi olmayabilir.

  2. Değişken iyalnızca döngü ve döngü için yereldir. Döngü bir veya daha fazla iş parçacığı çalışmadan önce biterse, bu iş parçacıklarının ömrü sona eren bir değişken için geçersiz bir başvuru olacaktır.

Değişkeni başvuru yerine i değere göre yakalayarak her iki sorunu da çok basit bir şekilde çözebilirsiniz . Bu, her bir iş parçacığının değerin bir kopyasına sahip olacağı ve bu kopya her iş parçacığı için benzersiz bir şekilde oluşturulacağı anlamına gelir .


5

Başka bir şey:
Her zaman sıralı bir sıraya sahip olmak için beklemeyin: 0, 1, 2, 3, ... çünkü çok iş parçacıklı yürütme modunun bir özgüllüğü vardır: belirsizlik .

Belirsizlik, aynı programın aynı koşullar altında yürütülmesinin farklı bir sonuç verdiği anlamına gelir.

Bunun nedeni, işletim sisteminin iş parçacıklarını birkaç parametreye bağlı olarak bir yürütmeden diğerine farklı zamanlamasıdır: CPU yükü, diğer işlemlerin önceliği, olası sistem kesintileri, ...

Örneğin yalnızca 5 iş parçacığı içerir, bu nedenle iş parçacığı sayısını artırmaya çalışın ve örneğin işleme işlevine uyku koyun, sonucun bir yürütmeden diğerine farklı olabileceğini göreceksiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.