nečelím v tuto chvíli ani tak problému jako spíš nejasnosti. Mám dvě vlákna které provádějí operace nad stejnou globální proměnnou. Globální proměnná je atomického datového typu. Přesto se moc atomicky nechová. Výsledek tohoto programu je pokaždé jiný, ačkoli by měl být stejný což znamená že se mi tam rvou vlákna. Není mi moc jasné proč, neb přece používám atomický datový typ který by měl být ňák "olockovaný" ne? Je dost možné že my něco uniklo, přece jenom někdy anglické věty hádám takže se radši ptám.
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
std::atomic<unsigned long> result(0);
void work()
{
using namespace std;
for (int i=0;i < 10000000;++i)
{
if (result.is_lock_free()) /*result není nikdy zamčen ???*/
{
//++result; /*funguje jak má*/
result.store(result.load()+1); /*pokaždé jiný vysledek*/
}
else
{
cout << "Atom result je zamcen!" << endl;
}
}
}
int main()
{
using namespace std;
thread alphaThread(work);
thread betaThread(work);
alphaThread.join();
betaThread.join();
cout << result.load() << endl;
return 0;
}
No a zde je jádro mé otázky. Co se stane když místo atomické kapsle kolem longu použiji prostě jen long? Uniká mě totiž přesná funkce atomických datových typů. Je mi jasné že ++i je atomický zatímco i=i+1 už ne.
nemusi to byt nutne pravda... slo hlavne o to, ze si pouzil vice atomickych operaci a tim uz si nemel jednu atomickou - nahral sis do nejakeho jineho mista obsah promenne, pricetl jedna a pak ulozil - to uz muze byt kdykoliv preruseno... Jinak u normalnich promennych neni atomicita zarucena nikdy (zalezi jen na procesoru, cache, a tak dale) a museji se pak pouzivat zamky...
Zamky jsou oproti atomickym operacim vyrazne pomalejsi - to je duvod, proc se pouzivaji ty atomicke promenne. + se to taky postara o urcity side effekty co se tyce procesorove cache...
A kdyz pouzijes toto, tak hned uvidis, ze to neni proste ani trochu atomicke:
#include <atomic>
//std::atomic<unsigned long> result(0);
unsigned long result(0);
void work()
{
using namespace std;
for (int i=0;i < 10000000;++i)
{
++result; /*funguje jak má*/
//result.store(result.load()+1); /*pokaždé jiný vysledek*/
}
}
int main()
{
using namespace std;
thread alphaThread(work);
thread betaThread(work);
alphaThread.join();
betaThread.join();
//cout << result.load() << endl;
cout << result << endl;
return 0;
}
Nahlásit jako SPAM
IP: 213.220.241.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Už jsem to začal chápat, díky. Poslední otázka. Jak je vlastně vytvořena atomická operace? Třeba load() a nebo store(). Jsou využívány atomické instrukce procesoru a nebo je "atomicita" emulována nějak jinak?
#6Sefiros
jsou na to specialni instrukce, ktery pak atomicitu zaruci + specialni priznaky pro stranky cache - jestli jsou pouzivany pro cteni, tak ji muze mit vic jader v cache, pokud pro zapis tak uz jen jedno jadro v jeden okamzik a tak dale...
Nahlásit jako SPAM
IP: 213.220.241.–
Program vždy dělá to co naprogramujete, ne to co chcete...