8 Haziran 2020 Pazartesi

std::atomic Sınıfı

Giriş
Elimizde şöyle bir kod olsun
int i = 1;
int x = ++i;
Bu kod assembly ile muhtemelen şöyledir. i değişkenine erişim bir çok adımdan oluşur ve multi-threaded ortamda doğru çalışmaz.. 2,3 ve 4 numaraları adımları atomic yapmak mümkün. Bu iş için std::atomic kullanılır. Böylece mutex kullanmdan multi-threaded ortamda da kod doğru çalışır.
1. store 1 in i
2. read i as tmp
3. add 1 to tmp
4. store tmp in x
Hangi tipler için kullanabiliriz
Genel kuralın açıklaması şöyle.
std::atomic may be instantiated with any TriviallyCopyable type T satisfying both CopyConstructible and CopyAssignable. 
Trivially copyable tipler integral tiplerdir. Integral tiplerden kasıt cstdint dosyasında tanımlı tüm tipler. Bunlar char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_t, char32_t, wchar_t vs. olabilir.

Dolayısıyla std::atomic eğer C++20 ile kodlansaydı belki şöyle kodlanabilirdi.
template< class T > requires
  std::is_trivially_copyable_v<T> &&
  std::is_copy_constructible_v<T> &&
  std::is_move_constructible_v<T> &&
  std::is_copy_assignable_v<T> &&
  std::is_move_assignable_v<T>
struct atomic { ... };
Integral Olmayan Tipler
Integral olmayan tipler için kod derlense bile bir yerden sonra hata veriyor.

Örnek - std::atomic<std::shared_ptr<..>>
std::atomic TriviallyCopyable değil.
std::is_trivially_copyable<std::shared_ptr<int>>::value == false;
Ancak std::atomic işlemleri ile çalışması arzu ediliyor. Dolayısıyla bir istisna yapılmış ve std::share_ptr parametresi alan atomic_load(), atomic_store() gibi metodlar tanımlanmış.

Örnek - std::atomic<std::string>
Aşağıdaki kod yanlış.
#include <atomic>
#include <string>

int main()
{
  std::atomic<std::string> a1("127.0.0.1:41001");
  std::string ep1_1 = a1.load();
  std::string ep1_2 = a1.load();
  return 0;
}
Örnek - std::atomic<double>
load ve exchange metodları var. Diğer metodları kendimiz yazmalıyız.
std::atomic<double> foo{0};

void add_to_foo(double bar) {
  auto current = foo.load();
  while (!foo.compare_exchange_weak(current, current + bar))
    ;
}
Örnek - std::atomic<std::array<..>>
Bu kod std::is_trivially_copyable için true dönüyor. Ancak yapmayın çünkü performansı öldürür.
std::atomic<std::array<int,10>> myArray;
Açıklaması şöyle.
The power of atomic variables come from the fact that some processors can do their operations with one instruction. The C++ compiler will try to make your atomic operations happen in one instruction. If it fails, it'll initiate a bus lock, which is like a global lock of everything, until that array is updated. It's equivalent to a mutex that locks all your variables in your program. If you're concerned about performance, don't do that!

Hazır Typedef'ler
atomic sınıfı aslında bir template. Integral tipler için bir sürü typedef yapılmış. Template olarak kullanmanın bir anlamı yok. Yani şöyle bir kod yazmak anlamsız ancak örneklerde hep bu tür kodlar kullanılıyor.
std::atomic<bool>
std::atomic_int
- std::atomic_uint,
- std::atomic_llong,
std::atomic_bool

yazılarına bakabilirsiniz.

compare_exchange_weak metodu
Şöyle yaparız.
// Effect: x += n, returns old value of x.
int inc(int n, std::atomic<int> & x)
{
    int old_val = x.load();
    for (;;) {
        int new_val = old_val + n;
        if (x.compare_exchange_weak(old_val, new_val)) {
            return old_val;
        }
        // Note: If the exchange fails, old_val is updated
        //       to the current value of x.
    }
}
exchange metodu
Yeni bir değer atar, eski değeri döner. While, If gibi koşullarda kullanılabilir.

fetch_add metodu
Açıklaması şöyle.
Atomically replaces the current value with the result of arithmetic addition of the value and arg. The operation is read-modify-write operation. Memory is affected according to the value of order.
Şöyle yaparız.
std::atomic<int> num;
num.fetch_add (1, std::memory_order_relaxed);
fetch_sub metodu
Açıklaması şöyle
A fetch_sub operation with memory_order_acquire semantics doesn’t synchronize-with anything, even though it stores a value, because it isn’t a release operation. Likewise, a store can’t synchronize-with a fetch_or with memory_order_release semantics, because the read part of the fetch_or isn’t an acquire operation.
is_lock_free metodu
Şöyle yaparız.
std::atomic<foo> var;

std::cout << var.is_lock_free() << std::endl;
std::cout << sizeof(var) << std::endl;
Çıktı olarak şunu alırız.
0
16
load metodu
Atomic olarak değeri almak veya atamak için kullanılır. Convert operator altta load metodunu kullanırlar.
Assignment and access operations are atomic
load metodu - memory order
Örnek - memory_order_acquire
Şöyle yaparız
std::atomic<bool> x;

while (x.load(std::memory_order_acquire) == false)
{
  x.wait(false, std::memory_order_acquire);
}
Örnek - memory_order_relaxed
Şöyle yaparız.
atomic<int> x(0);
x.store(1,std::memory_order_relaxed);

 while(!x.load(memory_order_relaxed)) {...}
operator ++ metodu - pre increment
Elimizde bir tane atomic int ve bir const değer olsun.
std::atomic<int> a = 10;
int i = 20;
Belli bir sınırı geçince işlem yapmak istersek şöyle yaparız.
if( ++a > i )
{
  // a is still not guaranteed to be greater than i at this point,
  // but AT THE TIME OF INCREMENTING it did exceed i.
}
Bu metod altta şu çağrıyı yapar.
fetch_add(1);
operator -- metodu - pre decrement
Şöyle yaparız.
std::atomic<int> value(0);

--value;
store metodu
Assignment operator altta store metodunu kullanırlar.

store metodu - memory order
Örnek
Şöyle yaparı<
std::atomic<int> flag { 0 };

void writer_thread() {

  // release value x to reader thread
  flag.store(1, std::memory_order_release);
}
Örnek
Şöyle yaparız.
std::atomic<int> y(0);
void f() {
  auto order = std::memory_order_relaxed;
  y.store(1, order);
  ...
}
wait metodu
Açıklaması şöyle.
According to cppreference, in C++20 there are wait, notify_one, notify_all in std::atomic<T>. Looks like they make std::atomic<T> usable as a futex.
Örnek
Şöyle yaparız.
std::atomic<bool> x;

while (x.load(std::memory_order_acquire) == false)
{
  x.wait(false, std::memory_order_acquire);
}



Hiç yorum yok:

Yorum Gönder