এটি সম্পূর্ণরূপে সি -++ কে ডেটা রেস হিসাবে সংজ্ঞায়িত করে যা অনির্ধারিত আচরণের কারণ করে, এমনকি যদি কোনও সংকলক কোড তৈরি করে যা কিছু টার্গেট মেশিনে আপনি যা প্রত্যাশা করেছিলেন তা করে। আপনি ব্যবহার করতে হবে std::atomic
নির্ভরযোগ্য ফলাফলের জন্য, কিন্তু আপনার সাথে এটি ব্যবহার করতে পারেন memory_order_relaxed
যদি আপনি পুনঃক্রমবিন্যাস যত্ন সম্পর্কে না। কিছু উদাহরণ কোড এবং asm আউটপুট ব্যবহার করে নীচে দেখুন fetch_add
।
তবে প্রথমে, সংসদ ভাষায় প্রশ্নের অংশ:
যেহেতু নাম ++ একটি নির্দেশ ( add dword [num], 1
), তাই আমরা কি সিদ্ধান্ত নিতে পারি যে নাম ++ এই ক্ষেত্রে পারমাণবিক?
মেমরি-গন্তব্য নির্দেশাবলী (খাঁটি দোকানে ছাড়া) পাঠযোগ্য সংশোধন-রাইট অপারেশন একাধিক অভ্যন্তরীণ পদক্ষেপে ঘটতে হয় । কোনও আর্কিটেকচারাল রেজিস্ট্রার সংশোধিত হয় না, তবে সিপিইউ এটির ALU- এর মাধ্যমে প্রেরণ করার সময় তথ্যটি অভ্যন্তরীণভাবে ধারণ করতে হয় । আসল রেজিস্টার ফাইলটি এমনকি সরলতম সিপিইউর অভ্যন্তরে ডেটা স্টোরেজের কেবলমাত্র একটি ছোট্ট অংশ, ল্যাচসগুলির সাথে এক পর্যায়ের আউটপুটগুলি থাকে অন্য পর্যায়ে ইনপুট ইত্যাদি etc.
অন্যান্য সিপিইউগুলির স্মৃতি ক্রিয়াকলাপ লোড এবং স্টোরের মধ্যে বিশ্বব্যাপী দৃশ্যমান হতে পারে। অর্থাত add dword [num], 1
একটি লুপে চলমান দুটি থ্রেড একে অপরের স্টোরগুলিতে প্রবেশ করবে। ( একটি সুন্দর চিত্রের জন্য @ মার্গারেটের উত্তর দেখুন )। দুইটি থ্রেডের প্রতিটি থেকে 40k ইনক্রিমেন্টের পরে, কাউন্টারটি কেবল রিয়েল মাল্টি-কোর x86 হার্ডওয়্যারে কেবল 60 ডলার (80 কে নয়) বাড়িয়েছে।
"অ্যাটমিক", গ্রীক শব্দ থেকে যার অর্থ অবিভাজ্য, এর অর্থ হ'ল কোনও পর্যবেক্ষক পৃথক পদক্ষেপ হিসাবে অপারেশনটি দেখতে পাচ্ছেন না। সমস্ত বিটের একযোগে শারীরিক / বৈদ্যুতিকভাবে তাত্ক্ষণিকভাবে ঘটে যাওয়া বোঝা বা স্টোরের জন্য এটি অর্জনের একমাত্র উপায়, তবে এটি ALU অপারেশনের পক্ষেও সম্ভব নয়। X86-এর পারমাণবিকতার বিষয়ে আমার উত্তরটিতে খাঁটি লোড এবং খাঁটি স্টোরগুলি সম্পর্কে আমি আরও অনেক বিশদে গিয়েছিলাম , যখন এই উত্তরটি পঠন-সংশোধন-লেখায় ফোকাস করে।
lock
উপসর্গ সমগ্র অপারেশন সিস্টেমের মধ্যে সম্ভাব্য সব পর্যবেক্ষক থেকে সম্মান সঙ্গে পারমাণবিক করতে অনেক পাঠযোগ্য সংশোধন-রাইট (মেমরি গন্তব্য) নির্দেশাবলীর প্রয়োগ করা যেতে পারে (অন্যান্য কোর এবং, DMA ডিভাইস, না একটি ফুটিয়ে তোলা যায় সিপিইউ পিনের আপ লাগানো)। যে কারণে এটি বিদ্যমান। ( এই প্রশ্নোত্তরও দেখুন )
সুতরাং lock add dword [num], 1
হয় পারমাণবিক । একটি সিপিইউ কোর যে নির্দেশটি চালাচ্ছে সেটি ক্যাশ লাইনটিকে তার ব্যক্তিগত এল 1 ক্যাশে পরিবর্তিত অবস্থায় রেখে দেবে যখন লোডটি ক্যাশে থেকে ডেটা পড়বে ততক্ষণ স্টোর তার ফলাফলকে ক্যাশে ফেরত পাঠায় না। এটি MESI ক্যাশে কোহেরেন্সি প্রোটোকলের নিয়ম অনুযায়ী (বা মাল্টি-কোর এএমডি / দ্বারা ব্যবহৃত MOESI / MESIF সংস্করণগুলির নিয়ম অনুসারে) সিস্টেমের অন্য কোনও ক্যাশে লোড থেকে স্টোর পর্যন্ত যে কোনও সময়ে ক্যাশে লাইনের অনুলিপি থেকে বাধা দেয় ts যথাক্রমে ইন্টেল সিপিইউ)। সুতরাং, অন্যান্য কোর দ্বারা পরিচালিত হয় আগে বা পরে ঘটেছিল সময়কালে হয় না।
lock
উপসর্গ ব্যতীত অন্য একটি কোর ক্যাশের লাইনের মালিকানা নিতে পারে এবং এটি আমাদের লোডের পরে কিন্তু আমাদের স্টোরের আগে সংশোধন করতে পারে, যাতে অন্য স্টোরটি আমাদের লোড এবং স্টোরের মধ্যে বিশ্বব্যাপী দৃশ্যমান হয়। আরও বেশ কয়েকটি উত্তর এটি ভুল পেয়েছে এবং দাবি করে যে lock
আপনি একই ক্যাশে লাইনের বিরোধী অনুলিপিগুলি না পেলে। সুসংগত ক্যাশেযুক্ত সিস্টেমে এটি কখনই ঘটতে পারে না।
(যদি কোনও lock
এড নির্দেশিকা মেমোরিতে দুটি ক্যাস লাইন বিস্তৃত থাকে তবে অবজেক্টের উভয় অংশের পরিবর্তনগুলি সকল পর্যবেক্ষকের কাছে বর্ধিত হওয়ার সাথে সাথে এটি পরমাণু থেকে যায় তা নিশ্চিত করতে আরও অনেক বেশি কাজ লাগে, সুতরাং কোনও পর্যবেক্ষক ছিঁড়ে দেখতে পাবে না। সিপিইউ হতে পারে ডেটা মেমরিটিকে আঘাত না করা পর্যন্ত পুরো মেমোরি বাসটি লক করতে হবে your আপনার পারমাণবিক ভেরিয়েবলগুলি ভুলভাবে চিহ্নিত করবেন না!)
নোট করুন যে lock
উপসর্গটি একটি নির্দেশকে একটি সম্পূর্ণ মেমরি বাধা হিসাবে রূপান্তরিত করে ( এমএফইএনএনসিএনএসির মতো ), সমস্ত রান-টাইম পুনরায় অর্ডারিং বন্ধ করে এবং এভাবে ধারাবাহিক ধারাবাহিকতা দেয়। (দেখুন জেফ Preshing এর চমৎকার ব্লগ পোস্টে । তার অন্যান্য পোস্ট আছে সব চমৎকার, খুব, এবং স্পষ্ট একটি ব্যাখ্যা অনেক সম্পর্কে ভাল জিনিস লক-বিনামূল্যে প্রোগ্রামিং , x86 ও সি ++ নিয়ম অন্যান্য হার্ডওয়্যার বিবরণ থেকে।)
একটি uniprocessor মেশিন উপর, অথবা একটি একক থ্রেডেড প্রক্রিয়ায় , একটি একক RMW নির্দেশ আসলে হয় একটি ছাড়া পারমাণবিক lock
উপসর্গ। ভাগ করা ভেরিয়েবল অ্যাক্সেস করার জন্য অন্য কোডের একমাত্র উপায় হ'ল সিপিইউ একটি প্রসঙ্গ সুইচ করা, যা কোনও নির্দেশের মাঝামাঝি ঘটতে পারে না। সুতরাং একটি সমতল dec dword [num]
একক থ্রেডেড প্রোগ্রাম এবং এর সিগন্যাল হ্যান্ডলারগুলির মধ্যে বা একক-কোর মেশিনে চলমান বহু-থ্রেড প্রোগ্রামে সিঙ্ক্রোনাইজ করতে পারে। দেখুন অন্য প্রশ্নে আমার উত্তর দ্বিতীয়ার্ধে , এবং এটি অধীনে মন্তব্য, যেখানে আমি বিশদ আলোচনা করা এই ব্যাখ্যা।
সি ++ এ ফিরুন:
সংকলকটি না জানিয়ে num++
এটি ব্যবহার করা সম্পূর্ণরূপে বোগাস যা আপনার প্রয়োজন একটি একক পঠন-পরিবর্তন-লিখন প্রয়োগের জন্য সংকলন করার:
;; Valid compiler output for num++
mov eax, [num]
inc eax
mov [num], eax
এটি যদি আপনি num
পরে এর মান ব্যবহার করেন তবে সম্ভবত : সংকলক বর্ধিত হওয়ার পরে এটি একটি রেজিস্টারে লাইভ রাখবে। সুতরাং আপনি num++
নিজের থেকে কীভাবে সংকলন করে তা পরীক্ষা করে দেখুন, পার্শ্ববর্তী কোডটি পরিবর্তন করা এটিকে প্রভাবিত করতে পারে।
(যদি মানটির পরে প্রয়োজন না হয় তবে তা inc dword [num]
পছন্দসই হয়; আধুনিক x86 সিপিইউগুলি তিনটি পৃথক নির্দেশনা ব্যবহার করার মতো কমপক্ষে দক্ষতার সাথে একটি মেমরি-গন্তব্য আরএমডাব্লু নির্দেশ পরিচালনা gcc -O3 -m32 -mtune=i586
করবে Fun মজাদার ঘটনা: আসলে এটি নির্গত হবে , কারণ (পেন্টিয়াম) পি 5 এর সুপারক্যালার পাইপলাইনটি না একাধিক সাধারণ মাইক্রো-অপারেশনগুলির জটিল নির্দেশগুলি পি 6 এবং পরবর্তী মাইক্রোআরকিটেকচারগুলি ডিকোড করবেন না more আরও তথ্যের জন্য অ্যাগনার ফগের নির্দেশ সারণী / মাইক্রোআরকিটেকচার গাইড এবং দেখুনএক্স 86 অনেকগুলি দরকারী লিঙ্কের জন্য ট্যাগ উইকি ট্যাগ করুন (ইন্টেলের x86 আইএসএ ম্যানুয়ালগুলি, যা পিডিএফ হিসাবে নির্বিঘ্নে উপলব্ধ)।
লক্ষ্য মেমরি মডেল (x86) কে সি ++ মেমরি মডেলের সাথে গুলিয়ে ফেলবেন না
সংকলন-সময় পুনর্নির্মাণ অনুমোদিত । আপনি স্টাড :: পারমাণবিক সাথে যা পান তার অন্য অংশটি কেবল অন্যnum++
কোনও ক্রিয়াকলাপের পরেআপনারবিশ্বব্যাপী দৃশ্যমানহয় তা নিশ্চিত করার জন্য, সংকলন-সময় পুনর্নির্মাণের উপর নিয়ন্ত্রণ।
ক্লাসিক উদাহরণ: আরেকটি থ্রেড দেখার জন্য বাফারে কিছু ডেটা সংরক্ষণ করা, তারপরে একটি পতাকা সেট করা। যদিও x86 নিখরচায় লোড / রিলিজ স্টোর অর্জন করে, তবুও আপনাকে কম্পাইলারটি ব্যবহার করে পুনরায় অর্ডার না করতে বলতে হবে flag.store(1, std::memory_order_release);
।
আপনি আশা করতে পারেন যে এই কোডটি অন্য থ্রেডের সাথে সিঙ্ক্রোনাইজ করবে:
// flag is just a plain int global, not std::atomic<int>.
flag--; // This isn't a real lock, but pretend it's somehow meaningful.
modify_a_data_structure(&foo); // doesn't look at flag, and the compilers knows this. (Assume it can see the function def). Otherwise the usual don't-break-single-threaded-code rules come into play!
flag++;
তবে তা হবে না। flag++
সংকলকটি ফাংশন কল জুড়ে সরানোর জন্য নিখরচায় (যদি এটি ফাংশনটির সাথে ইনলাইন করে থাকে বা জানে যে এটি তাকান না flag
)। তারপরে এটি পুরোপুরি পরিবর্তনটি অপ্টিমাইজ করতে পারে, কারণ flag
এটি এমনকি নয় volatile
। (এবং না, সি ++ volatile
স্ট্যান্ডার্ড :: পারমাণবিক। এসটিডি :: পারমাণবিকের জন্য কোনও কার্যকর বিকল্প নয়, এটি কম্পাইলারকে ধরে নিয়েছে যে মেমরির মানগুলি সংকীর্ণভাবে অনুরূপভাবে সংশোধন করা যেতে পারে volatile
, তবে এর চেয়ে আরও অনেক কিছু রয়েছে Also এছাড়াও, volatile std::atomic<int> foo
এটি নয় একই হিসাবে std::atomic<int> foo
, যেমন @Richard Hodges সঙ্গে আলোচনা করেছেন।)
অপ্রচলিত আচরণ হিসাবে অ-পারমাণবিক ভেরিয়েবলের উপর ডেটা রেসের সংজ্ঞা দেওয়া হ'ল সংকলকটি এখনও লুপগুলি উত্তোলন করতে এবং লুপের বাইরে ডুবন্ত স্টোরকে অনুমতি দেয় এবং মেমরির জন্য আরও অনেক অপ্টিমাইজেশন যা একাধিক থ্রেডের রেফারেন্স থাকতে পারে। ( ইউবি কীভাবে সংকলক অপ্টিমাইজেশান সক্ষম করে সে সম্পর্কে আরও জানার জন্য এই এলএলভিএম ব্লগটি দেখুন ))
যেমনটি আমি উল্লেখ করেছি, x86 lock
উপসর্গটি একটি সম্পূর্ণ মেমরি বাধা, তাই num.fetch_add(1, std::memory_order_relaxed);
x86 এ একই কোডটি উত্পন্ন করে num++
(ডিফল্টটি ক্রমযুক্ত ধারাবাহিকতা হয়) তবে এটি অন্যান্য আর্কিটেকচারে (যেমন এআরএম) আরও কার্যকর হতে পারে। এমনকি x86-এও রিল্যাক্সড আরও সংকলন-সময় পুনরায় অর্ডারিংয়ের অনুমতি দেয়।
std::atomic
গ্লোবাল ভেরিয়েবলের উপর পরিচালিত কয়েকটি ফাংশনের জন্য এটি জিসিসি আসলে x86 এ করে ।
গডবোল্ট সংকলক এক্সপ্লোরারটিতে উত্স + অ্যাসেম্বলি ভাষার কোডটি সুন্দরভাবে ফর্ম্যাট করা দেখুন । আপনি এই লক্ষ্যগুলির জন্য অ্যাটমিক্স থেকে কী ধরণের সমাবেশ ভাষা কোড পান তা দেখতে আপনি এআরএম, এমআইপিএস এবং পাওয়ারপিসি সহ অন্যান্য টার্গেট আর্কিটেকচার নির্বাচন করতে পারেন।
#include <atomic>
std::atomic<int> num;
void inc_relaxed() {
num.fetch_add(1, std::memory_order_relaxed);
}
int load_num() { return num; } // Even seq_cst loads are free on x86
void store_num(int val){ num = val; }
void store_num_release(int val){
num.store(val, std::memory_order_release);
}
// Can the compiler collapse multiple atomic operations into one? No, it can't.
# g++ 6.2 -O3, targeting x86-64 System V calling convention. (First argument in edi/rdi)
inc_relaxed():
lock add DWORD PTR num[rip], 1 #### Even relaxed RMWs need a lock. There's no way to request just a single-instruction RMW with no lock, for synchronizing between a program and signal handler for example. :/ There is atomic_signal_fence for ordering, but nothing for RMW.
ret
inc_seq_cst():
lock add DWORD PTR num[rip], 1
ret
load_num():
mov eax, DWORD PTR num[rip]
ret
store_num(int):
mov DWORD PTR num[rip], edi
mfence ##### seq_cst stores need an mfence
ret
store_num_release(int):
mov DWORD PTR num[rip], edi
ret ##### Release and weaker doesn't.
store_num_relaxed(int):
mov DWORD PTR num[rip], edi
ret
ক্রম-ধারাবাহিকতা স্টোরগুলির পরে কীভাবে এমএফএনসিইএন (সম্পূর্ণ বাধা) দরকার তা লক্ষ করুন। x86 সাধারণভাবে দৃ general়ভাবে অর্ডার করা হয় তবে স্টোরলড পুনরায় অর্ডার করার অনুমতি দেওয়া হয়। পাইপলাইনের আউট-অফ-অর্ডার সিপিইউতে ভাল পারফরম্যান্সের জন্য স্টোর বাফার থাকা অপরিহার্য। আইনটিতে জেফ প্রেসিংয়ের মেমোরি রির্ডারিং ক্যাচড রিয়েলর্ডিং রিয়েল হার্ডওয়ারে ঘটতে দেখানোর জন্য রিয়েল কোড সহ এমএফএনএনসিইএস ব্যবহার না করার পরিণতিগুলি দেখায় ।
পুনরায়: স্টাইল :: পারমাণবিক num++; num-=2;
ক্রিয়াকলাপগুলিকে এক num--;
নির্দেশে মার্জ করে সংকলকগণ সম্পর্কে @ রিচার্ড হজেসের উত্তরের মন্তব্যে আলোচনা :
এই একই বিষয়ে একটি পৃথক প্রশ্নোত্তর: কেন কম্পাইলারগুলি রিলান্ট্যান্ট স্টাড :: পারমাণবিক লেখায় একীভূত হয় না? , যেখানে আমার উত্তরটি নীচে আমি যা লিখেছি তা অনেকটা পুনরুদ্ধার করে।
বর্তমান সংকলকরা আসলে এটি (এখনও) করেন না, তবে তাদের অনুমতি দেওয়া হয়নি বলে নয়। সি ++ ডাব্লুজি 21 / P0062R1: সংকলকরা কখন পারমাণবিকতা অনুকূল করতে হবে? অনেক প্রোগ্রামারদের এমন প্রত্যাশা নিয়ে আলোচনা করা হয় যে সংকলকরা "আশ্চর্যজনক" অপ্টিমাইজেশান তৈরি করে না, এবং প্রোগ্রামারদের নিয়ন্ত্রণ দেওয়ার জন্য স্ট্যান্ডার্ড কী করতে পারে। N4455 এমন অনেকগুলি বিষয় নিয়ে আলোচনা করেছে যা এইগুলি সহ অনুকূলিত করা যায়। এটি উল্লেখ করে যে অন্তর্নিহিত এবং ধ্রুবক-প্রসারণ এমন কিছু জিনিস প্রবর্তন fetch_or(0)
করতে পারে যা load()
মূল উত্সটিতে কোনও স্পষ্টত অপ্রয়োজনীয় পারমাণবিক অপ্স না থাকলেও কেবলমাত্র (তবে এখনও শব্দার্থকে অর্জন ও প্রকাশ করতে পারে) রূপান্তর করতে সক্ষম হতে পারে ।
সংকলকগণ এটি না করার (এখনও) হ'ল: (১) জটিল জটিল কোডটি কেউ লেখেনি যে সংকলকটি নিরাপদে এটি করতে দেয় (কখনও এটি ভুল না করে), এবং (২) এটি সম্ভাব্যত ন্যূনতম নীতির লঙ্ঘন করে অবাক । লক-ফ্রি কোডটি প্রথম স্থানে সঠিকভাবে লেখার পক্ষে যথেষ্ট শক্ত। সুতরাং আপনার পারমাণবিক অস্ত্রের ব্যবহারে নৈমিত্তিক হবেন না: সেগুলি সস্তা নয় এবং বেশি অনুকূলিতকরণ করে না। অপ্রয়োজনীয় পারমাণবিক ক্রিয়াকলাপগুলি এড়ানো সর্বদা সহজ নয় std::shared_ptr<T>
, যদিও এটির কোনও অ-পরমাণু সংস্করণ নেই (যদিও এখানে উত্তরগুলির মধ্যে একটি shared_ptr_unsynchronized<T>
জিসিসি জন্য একটি সংজ্ঞায়নের একটি সহজ উপায় দেয় )।
এ ফেরা num++; num-=2;
সংকলন যেমন যদি এটা ছিল num--
কম্পাইলার: অনুমতি দেওয়া হয় এই কাজ করতে, যদি না num
হয় volatile std::atomic<int>
। যদি একটি পুনঃনির্মাণ করা সম্ভব হয় তবে বিস্মিত বিধি নিয়ম সংকলককে সংকলনের সময় সিদ্ধান্ত নিতে দেয় যে এটি সর্বদা সেভাবেই ঘটে। কোনও পর্যবেক্ষক অন্তর্বর্তী মানগুলি ( num++
ফলাফল) দেখতে পাবে এমন কোনও গ্যারান্টি নেই ।
উদাহরণস্বরূপ, যদি এই ক্রিয়াকলাপগুলির মধ্যে বিশ্বব্যাপী কিছুই দৃশ্যমান না হয় এমন ক্রম উত্সের ক্রম প্রয়োজনীয়তার সাথে সামঞ্জস্যপূর্ণ (লক্ষ্য স্থিতি নয়, বিমূর্ত মেশিনের সি ++ নিয়ম অনুসারে), সংকলকটি / এর lock dec dword [num]
পরিবর্তে একটি একক নির্গত করতে পারে ।lock inc dword [num]
lock sub dword [num], 2
num++; num--
অদৃশ্য হতে পারে না, কারণ এটি এখনও অন্য থ্রেডগুলির সাথে সম্পর্কের সাথে একটি সিঙ্ক্রোনাইজ করে num
এবং এটি একটি অর্জন-লোড এবং একটি রিলিজ-স্টোর উভয়ই যা এই থ্রেডের অন্যান্য ক্রিয়াকলাপের পুনঃক্রমকে অস্বীকার করে। X86 এর জন্য, এটি কোনও lock add dword [num], 0
(ie num += 0
) এর পরিবর্তে কোনও MFENCE- এ সংকলন করতে সক্ষম হতে পারে ।
PR0062 এ আলোচিত হিসাবে , সংকলনের সময় অ-সংলগ্ন পারমাণবিক অপের আরও আক্রমণাত্মক সংযোজন খারাপ হতে পারে (উদাহরণস্বরূপ একটি অগ্রগতি কাউন্টার কেবল প্রতিটি পুনরাবৃত্তির পরিবর্তে একবারে আপডেট হয়), তবে এটি ডাউনসাইড ছাড়াই পারফরম্যান্সে সহায়তা করতে পারে (উদাহরণস্বরূপ এড়িয়ে যাওয়া) পারমাণবিক ইনক / ডিসি রেফ গণনা করা হয় যখন কোনওটির অনুলিপি shared_ptr
তৈরি করা হয় এবং ধ্বংস হয়, যদি সংকলক প্রমাণ করতে পারে যে shared_ptr
অস্থায়ী পুরো জীবনকাল জন্য অন্য কোনও বস্তুর উপস্থিতি রয়েছে))
এমনকি num++; num--
একত্রিত হওয়া কোনও লক প্রয়োগের ন্যায্যতার ক্ষতি করতে পারে যখন একটি থ্রেড এখনই আনলক হয় এবং পুনরায় লক হয়। এটি প্রকৃতপক্ষে কখনই প্রকাশিত হয় না, এমনকি হার্ডওয়্যার আরবিট্রেশন প্রক্রিয়াগুলি অন্য থ্রেডটিকে সেই সময়ে লকটি ধরার সুযোগ দেয় না।
বর্তমান জিসিসি .2.২ এবং ক্ল্যাং ৩.৯ সহ, আপনি এখনও সর্বাধিক স্পষ্টতই অপটিমাইজযোগ্য ক্ষেত্রে পৃথক lock
এড ক্রিয়াকলাপগুলি memory_order_relaxed
পান। ( গডবোল্ট সংকলক এক্সপ্লোরার যাতে আপনি দেখতে পারেন যে সর্বশেষতম সংস্করণগুলি আলাদা কিনা if)
void multiple_ops_relaxed(std::atomic<unsigned int>& num) {
num.fetch_add( 1, std::memory_order_relaxed);
num.fetch_add(-1, std::memory_order_relaxed);
num.fetch_add( 6, std::memory_order_relaxed);
num.fetch_add(-5, std::memory_order_relaxed);
//num.fetch_add(-1, std::memory_order_relaxed);
}
multiple_ops_relaxed(std::atomic<unsigned int>&):
lock add DWORD PTR [rdi], 1
lock sub DWORD PTR [rdi], 1
lock add DWORD PTR [rdi], 6
lock sub DWORD PTR [rdi], 5
ret
add
পারমাণবিক?