Bezbolesne Programowanie Współbieżne Konrad Siek 26 III 2014
Lata 90-te???
Częstotliwość procesorów Tranzystory 0 500 1000 1500 1985 1990 1995 2000 Rok
Lata 90-te My new computer s got the clocks, it rocks, but it was obsolete before I opened the box. You say you ve had your desktop for over a week? Throw that junk away, man, it s an antique. Your laptop is a month old? Well, that s great. If you could use a nice, heavy paperweight... It s all about the Pentiums, Weird Al, 1999
Częstotliwość procesorów Taktowanie [MHz] 0 1000 3000 1985 1990 1995 2000 2005 2010 Rok
Prawo Moore-a in action Tranzystory 0.0e+00 1.5e+09 1985 1990 1995 2000 2005 2010 Rok
Liczby rdzeni w procesorach Rdzenie 2 4 6 8 10 1985 1990 1995 2000 2005 2010 Rok
Od rdzeni Współbieżność
Od rdzeni Współbieżność... ale współbieżność boli
... boli?
Rzeczy których nie lubie Szukanie bugów (bagów?) Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Rzeczy których nie lubie Szukanie bugów (bagów?) Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Rzeczy których nie lubie Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Heisenbug
Rzeczy których nie lubie Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Rzeczy których nie lubie Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Wrzucanie kodu strukturalnego do funkcjonalnego class Elem: def init (self, transaction, t, next): self.transaction = transaction self.t = t self.next = next _head = None def insert(transaction, t): if _head is None: _head = Elem(transaction, t, None) return elem = _head while True: if elem.t <= t: temp = Elem(elem.transaction, elem.t, elem.next) elem.transaction = transaction elem.t = t elem.next = temp return else: if elem.next: elem = elem.next else: elem.next = Elem(transaction, t, None) return
Wrzucanie kodu strukturalnego do funkcjonalnego from threading import RLock, Condition class Elem: def init (self, transaction, t, next): self.transaction = transaction self.t = t self.next = next _head = None _global_lock = Condition() _transaction_lock = RLock() def insert(head, transaction, t): elem = head while True: if elem.t <= t: temp = Elem(elem.transaction, elem.t, elem.next) elem.transaction = transaction elem.t = t elem.next = temp return else: if elem.next: elem = elem.next else: elem.next = Elem(transaction, t, None) return
Wrzucanie kodu strukturalnego do funkcjonalnego from threading import RLock, Condition class Elem: def init (self, transaction, t, next): self.transaction = transaction self.t = t self.next = next _head = None _global_lock = Condition() _transaction_lock = RLock() def insert(head, transaction, t): elem = head while True: if elem.t <= t: temp = Elem(elem.transaction, elem.t, elem.next) elem.transaction = transaction elem.t = t elem.next = temp return else: if elem.next: elem = elem.next else: elem.next = Elem(transaction, t, None) return def lock(transaction, length): _global_lock.acquire() if _head is None: acquired = _transaction_lock.acquire(blocking=false) if acquired and _head is None: _global_lock.release() return else: if _head == null: _head = Elem(T, t, null) else: insert(_head, T, t) while True: _global_lock.wait() if _head.t = T: _transaction_lock.acquire() if _head is not None: _head = _head.next _global_lock.release() return else: _global_lock.notify() def unlock(): _global_lock.acquire() _transaction_lock.release() _global_lock.notify() _global_lock.release() return
Rzeczy których nie lubie Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Rzeczy których nie lubie Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Rozwiązywanie problemów synchronizacji
Rozwiązywanie problemów synchronizacji
Rzeczy których nie lubie Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie problemów synchronizacji
Rzeczy których nie lubie Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie 2 problemów jednocześnie
Rozwiązania których szukam Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie 2 problemów jednocześnie
Rozwiązania których szukam Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Rozwiązywanie 2 problemów jednocześnie Uniwersalne
Rozwiązania którzych szukam Szukanie Heisenbugów Wrzucanie kodu strukturalnego do funkcjonalnego Proste w implementacji Rozwiązywanie 2 problemów jednocześnie Uniwersalne
Rozwiązania których szukam Szukanie Heisenbugów Bezbłędne Wrzucanie kodu strukturalnego do funkcjonalnego Proste w implementacji Rozwiązywanie 2 problemów jednocześnie Uniwersalne
Rozwiązania których szukam Szukanie Heisenbugów Bezbłędne Wrzucanie kodu strukturalnego do funkcjonalnego Proste w implementacji Rozwiązywanie 2 problemów jednocześnie Uniwersalne Wydajne
Rozwiązania
Globalny zamek from threading import Thread, Lock _global_lock = Lock() _shared_data = [0, 0, 0, 0] class GLThread (Thread): def init (self, data, index): Thread. init (self) self.index = index self.data = data def run(self): with _global_lock: self.data[self.index] += 1 my_data = self.data[self.index] print( set, self.index, to, my_data) for i in range(0, 10): GLThread(_shared_data, i % len(_shared_data)).start()
Globalny zamek from threading import Thread, Lock _global_lock = Lock() _shared_data = [0, 0, 0, 0] class GLThread (Thread): def init (self, data, index): Thread. init (self) self.index = index self.data = data def run(self): with _global_lock: self.data[self.index] += 1 my_data = self.data[self.index] print( set, self.index, to, my_data) for i in range(0, 10): GLThread(_shared_data, i % len(_shared_data)).start()
Globalny zamek Bezbolesna współbieżność
Globalny zamek Bezbolesna współbieżność
Globalny zamek Bezbolesna współbieżność
Wątki funkcyjne
Duplikaty
Duplikaty from hashlib import md5 hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() for i in range(0, len(files)): make_hash(i)
Duplikat from hashlib import md5 from _thread import start_new_thread # python2: thread hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() for i in range(0, len(files)): start_new_thread(function=make_hash, args=(i,))
Duplikat from hashlib import md5 from _thread import start_new_thread # python2: thread hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() for i in range(0, len(files)): start_new_thread(function=make_hash, args=(i,))
Global Interpreter Lock A global interpreter lock (GIL) is used internally to ensure that only one thread runs in the Python VM at a time. In general, Python offers to switch among threads only between bytecode instructions; how frequently it switches can be set via sys.setswitchinterval(). Each bytecode instruction and therefore all the C implementation code reached from each instruction is therefore atomic from the point of view of a Python program. http://docs.python.org/3.3/library/multiprocessing.html
Duplikaty from hashlib import md5 #from _thread import start_new_thread # python2: thread from multiprocessing import Process hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() for i in range(0, len(files)): #start_new_thread(function=make_hash, args=(i,)) Process(target=make_hash, args=(i,)).start()
Duplikaty from hashlib import md5 from multiprocessing import Process hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() for i in range(0, len(files)): Process(target=make_hash, args=(i,)).start()
Duplikaty
Duplikaty
Duplikaty from hashlib import md5 from multiprocessing import Process hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() for i in range(0, len(files)): Process(target=make_hash, args=(i,)).start()
Duplikaty from hashlib import md5 from multiprocessing import Pool hashes = [None] * len(files) def make_hash(i): with semaphore: source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() pool = Pool(processes=4) # define after make_hash for i in range(0, len(files)): pool.apply_async(func=make_hash, args=(i,))
Duplikaty from hashlib import md5 from multiprocessing import Pool hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() pool = Pool(processes=4) # define after make_hash for i in range(0, len(files)): pool.apply_async(func=make_hash, args=(i,)) pool.close() # close before joining pool.join()
Wątki funkcyjne Preferują solipsyzm
Wątki funkcyjne Preferują solipsyzm tylko odczyt bez konfliktów
Wątki funkcyjne Preferują solipsyzm tylko odczyt bez konfliktów Niski koszt
Wątki funkcyjne Preferują solipsyzm tylko odczyt bez konfliktów Niski koszt ten sam schemat <10 lini kodu
Wątki funkcyjne Preferują solipsyzm tylko odczyt bez konfliktów Niski koszt ten sam schemat <10 lini kodu Znaczna poprawa efektywności
Wątki funkcyjne (Python) from hashlib import md5 from multiprocessing import Pool hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() pool = Pool(processes=4) # define after make_hash for i in range(0, len(files)): pool.apply_async(func=make_hash, args=(i,)) pool.close() # close before joining pool.join()
Wątki funkcyjne (Python) map from hashlib import md5 from multiprocessing import Pool hashes = [None] * len(files) def make_hash(i): source = open(files[i], rb ) data = source.read() source.close() hashes[i] = md5(data).digest() pool = Pool(processes=4) # define after make_hash pool.map(func=make_hash, iterable=range(0, len(files)))
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna
Pamięć transakcyjna Python: Kamaelia/Axon (www.kamaelia.org) Durus (www.mems-exchange.org/software/durusworks/) Java Deuce STM (www.deucestm.org) C SXM (research.microsoft.com) C/C++ wbudowana w GCC (gcc.gnu.org/wiki/transactionalmemory)
Bank
Bank ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {}
Bank ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): for i in ids: accounts[i] = sum def total(ids): total = 0 for i in ids: total += accounts[i] return total def transfer(a, b, sum): accounts[a] -= sum accounts[b] += sum
Bank from random import choice, randint ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): for i in ids: accounts[i] = sum init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 transfer(a, b, sum) print total(ids) def total(ids): total = 0 for i in ids: total += accounts[i] return total def transfer(a, b, sum): accounts[a] -= sum accounts[b] += sum
Bank from random import choice, randint ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): # transaction start for i in ids: accounts[i] = sum # transaction end init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 # fork process transfer(a, b, sum) # join processes print total(ids) def total(ids): # transaction start total = 0 for i in ids: total += accounts[i] # transaction end return total def transfer(a, b, sum): # transaction start accounts[a] -= sum accounts[b] += sum # transaction end
Bank from random import choice, randint from multiprocessing import Pool ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): # transaction start for i in ids: accounts[i] = sum # transaction end init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids) def total(ids): # transaction start total = 0 for i in ids: total += accounts[i] # transaction end return total def transfer(a, b, sum): # transaction start accounts[a] -= sum accounts[b] += sum # transaction end
Bank from random import choice, randint from multiprocessing import Pool from durus.connection import Connection from durus.client_storage import ClientStorage ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): # transaction start for i in ids: accounts[i] = sum # transaction end init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids) def total(ids): # transaction start total = 0 for i in ids: total += accounts[i] # transaction end return total def transfer(a, b, sum): # transaction start accounts[a] -= sum accounts[b] += sum # transaction end
Bank from random import choice, randint from multiprocessing import Pool from durus.connection import Connection from durus.client_storage import ClientStorage ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): transaction = Connection(ClientStorage()) accounts = transaction.get_root() for i in ids: accounts[i] = sum transaction.commit() def total(ids): transaction = Connection(ClientStorage()) accounts = transaction.get_root() total = 0 for i in ids: total += accounts[i] transaction.commit() def transfer(a, b, sum): transaction = Connection(ClientStorage()) accounts = transaction.get_root() accounts[a] -= sum accounts[b] += sum transaction.commit() init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids) return total
Bank from random import choice, randint from multiprocessing import Pool from durus.connection import Connection from durus.client_storage import ClientStorage, ConflictError ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): transaction = Connection(ClientStorage()) accounts = transaction.get_root() for i in ids: accounts[i] = sum transaction.commit() def total(ids): transaction = Connection(ClientStorage()) accounts = transaction.get_root() total = 0 for i in ids: total += accounts[i] transaction.commit() return total def transfer(a, b, sum): transaction = Connection(ClientStorage()) accounts = transaction.get_root() try: accounts[a] -= sum accounts[b] += sum transaction.commit() except ConflictError: transaction.abort() init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids)
Bank from random import choice, randint from multiprocessing import Pool from durus.connection import Connection from durus.client_storage import ClientStorage, ConflictError ACCOUNTS = 10 TRANSFERS = 10 ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def init(ids, sum): transaction = Connection(ClientStorage()) accounts = transaction.get_root() for i in ids: accounts[i] = sum transaction.commit() def total(ids): transaction = Connection(ClientStorage()) accounts = transaction.get_root() total = 0 for i in ids: total += accounts[i] transaction.commit() return total def transfer(a, b, sum): transaction = Connection(ClientStorage()) accounts = transaction.get_root() while True: try: accounts[a] -= sum accounts[b] += sum transaction.commit() return except ConflictError: transaction.abort() init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids)
Bank class Transaction (Process): def init (self, f, args, kwargs={}): Process. init (self) self._f, self._a, self._kw = f, args, kwargs self.ppipe, self.pipe = Pipe() self._returned, self._result = False, None def run(self): transaction = Connection(ClientStorage()) while True: try: memory = transaction.get_root() result = self._f(memory, *self._a, **self._kw) transaction.commit() self.pipe.send(result) self.pipe.close() break except ConflictError: transaction.abort() def result(self): if not self._returned: self._result = self.ppipe.recv() self._returned = True return self._result
Bank class Transaction (Process): def init (self, f, args, kwargs={}): Process. init (self) self._f, self._a, self._kw = f, args, kwargs self.ppipe, self.pipe = Pipe() self._returned, self._result = False, None def run(self): transaction = Connection(ClientStorage()) while True: try: memory = transaction.get_root() result = self._f(memory, *self._a, **self._kw) transaction.commit() self.pipe.send(result) self.pipe.close() break except ConflictError: transaction.abort() def result(self): if not self._returned: self._result = self.ppipe.recv() self._returned = True return self._result # pool = Pool(processes=4) # pool = Pool(processes=4, initializer=transaction) t = Transaction(init, (ids, 1000)) t.start() t.join() ts = [] for i in range(0, TRANSFERS): #... t = Transaction(transfer, (a,b,sum)) # pool.apply_async(func=t.start) t.start() ts.append(t) for t in ts: t.join() t = Transaction(total, (ids,)) t.start() t.join() print t.result()
Bank class Transaction (Process): def init (self, f, args, kwargs={}): Process. init (self) self._f, self._a, self._kw = f, args, kwargs self.ppipe, self.pipe = Pipe() self._returned, self._result = False, None def run(self): transaction = Connection(ClientStorage()) while True: try: memory = transaction.get_root() result = self._f(memory, *self._a, **self._kw) transaction.commit() self.pipe.send(result) self.pipe.close() break except ConflictError: transaction.abort() def result(self): if not self._returned: self._result = self.ppipe.recv() self._returned = True return self._result # pool = Pool(processes=4) # pool = Pool(processes=4, initializer=transaction) t = Transaction(init, (ids, 1000)) t.start() t.join() ts = [] for i in range(0, TRANSFERS): #... t = Transaction(transfer, (a,b,sum)) # pool.apply_async(func=t.start) t.start() ts.append(t) for t in ts: t.join() t = Transaction(total, (ids,)) t.start() t.join() print t.result()
Bank def transaction(f, a, kw={}): transaction = Connection(ClientStorage()) while True: try: memory = transaction.get_root() result = f(memory, *a, **kw) transaction.commit() return result except ConflictError: transaction.abort()
Bank from random import choice, randint from multiprocessing import Pool from durus.connection import Connection from durus.client_storage import ClientStorage, ConflictError ACCOUNTS = 10 TRANSFERS = 10 def total(ids): def _total(accounts, ids): total = 0 for i in ids: total += accounts[i] return total return transaction(_total, (ids,)) ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def transaction(f, a, kw={}): transaction = Connection(ClientStorage()) while True: try: memory = transaction.get_root() result = f(memory, *a, **kw) transaction.commit() return result except ConflictError: transaction.abort() def init(ids, sum): def _init(accounts, ids, sum): for i in ids: accounts[i] = sum return transaction(_init, (ids, sum,)) def transfer(a, b, sum): def _transfer(accounts, a, b, sum): accounts[a] -= sum accounts[b] += sum return transaction(_transfer, (a, b, sum)) init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids)
Pamięć transakcyjna Optymistyczna
Pamięć transakcyjna Optymistyczna czynności są powtarzane jeśli są konflikty czuła na liczbę transakcji i zasobow extra kod
Pamięć transakcyjna Optymistyczna czynności są powtarzane jeśli są konflikty czuła na liczbę transakcji i zasobow extra kod Uniwersalność
Pamięć transakcyjna Optymistyczna czynności są powtarzane jeśli są konflikty czuła na liczbę transakcji i zasobow extra kod Uniwersalność ten sam schemat rozwiązuje większość problemów
Pamięć transakcyjna Optymistyczna czynności są powtarzane jeśli są konflikty czuła na liczbę transakcji i zasobow extra kod Uniwersalność ten sam schemat rozwiązuje większość problemów Poprawa efektywności
Pamięć transakcyjna Optymistyczna czynności są powtarzane jeśli są konflikty czuła na liczbę transakcji i zasobow extra kod Uniwersalność ten sam schemat rozwiązuje większość problemów Poprawa efektywności (jeśli nie ma dużo konfliktów)
Pamięć transakcyjna Optymistyczna czynności są powtarzane jeśli są konflikty czuła na liczbę transakcji i zasobow extra kod Uniwersalność ten sam schemat rozwiązuje większość problemów Poprawa efektywności (jeśli nie ma dużo konfliktów) Akcje muszą dać się powtarzać (i nie mogą mieć nieodwracalnych efektów)
Bank // Deuce STM // compile with -javaagent:bin/deuceagent.jar public class Bank { public static final int TRANSFERS = 10; public static final int ACCOUNTS = 100; } private final double[] accounts = new double[accounts]; public Bank() { for (int i = 0; i < accounts.length; i++) { accounts[i] = 200; } } @Atomic public void total() { double total = 0d; for (int i = 0; i < accounts.length; i++) { total += accounts[i]; } } @Atomic public void transfer(int a, int b, double sum) { accounts[a] -= sum; accounts[b] += sum; } public static void main(string[] args) { final Random random = new Random(); final Bank bank = new Bank(); List<Thread> transactions = new LinkedList<Thread>(); } for (int i = 0; i < Bank.TRANSFERS; i++) { final int a = random.nextint(bank.accounts); final int b = random.nextint(bank.accounts); final double sum = 5d + random.nextdouble() % 15d; Thread t = new Thread() { public void run() { bank.transfer(a, b, sum); } }; t.start(); transactions.add(t); } Thread t = new Thread() { public void run() { bank.total(); } }; t.start(); transactions.add(t); for (Thread thread : transactions) { thread.join(); }
Bank from random import choice, randint from multiprocessing import Pool from durus.connection import Connection from durus.client_storage import ClientStorage, ConflictError ACCOUNTS = 10 TRANSFERS = 10 def total(ids): def _total(accounts, ids): total = 0 for i in ids: total += accounts[i] return total return transaction(_total, (ids,)) ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def transaction(f, a, kw={}): transaction = Connection(ClientStorage()) while True: try: memory = transaction.get_root() result = f(memory, *a, **kw) transaction.commit() return result except ConflictError: transaction.abort() def init(ids, sum): def _init(accounts, ids, sum): for i in ids: accounts[i] = sum return transaction(_init, (ids, sum,)) def transfer(a, b, sum): def _transfer(accounts, a, b, sum): accounts[a] -= sum accounts[b] += sum return transaction(_transfer, (a, b, sum)) init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids)
Bank from random import choice, randint from multiprocessing import Pool from durus.connection import Connection from durus.client_storage import ClientStorage, ConflictError ACCOUNTS = 10 TRANSFERS = 10 def total(ids): def _total(accounts, ids): total = 0 for i in ids: total += accounts[i] return total return transaction(_total, (ids,)) ids = ["id_" + str(i) for i in range(0, ACCOUNTS)] accounts = {} def transaction(f, a, kw={}): transaction = Connection(ClientStorage()) while True: try: memory = transaction.get_root() result = f(memory, *a, **kw) transaction.commit() return result except ConflictError: transaction.abort() def init(ids, sum): def _init(accounts, ids, sum): for i in ids: accounts[i] = sum return transaction(_init, (ids, sum,)) def transfer(a, b, sum): def _transfer(accounts, a, b, sum): ask_user_for_authentication(a, b, sum) if _: accounts[a] -= sum accounts[b] += sum return transaction(_transfer, (a, b, sum)) init(ids, 1000) for i in range(0, TRANSFERS): a = choice(ids) b = choice(filter(lambda x: x!= a, ids)) sum = randint(1, 5) * 100 pool.apply_async(func=transfer, args=(a,b,sum)) pool.close() pool.join() print total(ids)
Globalny zamek!
Globalny zamek!
Posortowane zamki Pesymistyczne transakcje
Ale zamki są trudne?
Tak Ale zamki są trudne?
Ale zamki są trudne? Tak... ale dlaczego?
Ale zamki są trudne? Tak... ale dlaczego? Deadlock
Ale zamki są trudne? Tak... ale dlaczego? Deadlock Pamiętanie który zamek jest do czego i kompozycyjność
Ale zamki są trudne? Tak... ale dlaczego? Deadlock Pamiętanie który zamek jest do czego i kompozycyjność Dodatkowy kod
Ale zamki są trudne? Tak... ale dlaczego? Deadlock Pamiętanie który zamek jest do czego i kompozycyjność Dodatkowy kod zamknąć zamki w transakcje
Ale zamki są trudne? Tak... ale dlaczego? Deadlock Pamiętanie który zamek jest do czego i kompozycyjność po jednym zamku na obiekt współdzielony Dodatkowy kod zamknąć zamki w transakcje
Ale zamki są trudne? Tak... ale dlaczego? Deadlock sortowanie zamków, 2PL Pamiętanie który zamek jest do czego i kompozycyjność po jednym zamku na obiekt współdzielony Dodatkowy kod zamknąć zamki w transakcje
Posortowane zamki
Posortowane zamki
Posortowane zamki
Posortowane zamki
Posortowane zamki
Posortowane zamki
Posortowane zamki from multiprocessing import Lock accounts, locks = {}, {} for i in range(0, ACCOUNTS): accounts[i] = 200 locks[i] = Lock() def transfer(a, b, sum): _locks = [locks[a], locks[b]] _locks.sort(key = lambda x: str(x)) for lock in _locks: lock.acquire() accounts[a] -= sum locks[a].release() accounts[b] += sum locks[b].release()
Kiedy czego używać? Wątki funcyjne
Kiedy czego używać? Wątki funcyjne niezależne zadania
Kiedy czego używać? Wątki funcyjne niezależne zadania Transakcje
Kiedy czego używać? Wątki funcyjne niezależne zadania Transakcje wszystko co można powtarzać, bez bardzo dużej konkurencji o zasoby
Kiedy czego używać? Wątki funcyjne niezależne zadania Transakcje wszystko co można powtarzać, bez bardzo dużej konkurencji o zasoby Posortowane zamki
Kiedy czego używać? Wątki funcyjne niezależne zadania Transakcje wszystko co można powtarzać, bez bardzo dużej konkurencji o zasoby Posortowane zamki wszystko czego nie można powtarzać
Kiedy czego używać? Wątki funcyjne niezależne zadania Transakcje wszystko co można powtarzać, bez bardzo dużej konkurencji o zasoby Posortowane zamki wszystko czego nie można powtarzać Globalny zamek
Kiedy czego używać? Wątki funcyjne niezależne zadania Transakcje wszystko co można powtarzać, bez bardzo dużej konkurencji o zasoby Posortowane zamki wszystko czego nie można powtarzać Globalny zamek najlepiej nigdy
Kiedy czego używać? Wątki funcyjne niezależne zadania Transakcje wszystko co można powtarzać, bez bardzo dużej konkurencji o zasoby Posortowane zamki wszystko czego nie można powtarzać Globalny zamek najlepiej nigdy
Koło Naukowe SKiSR Koło Naukowe SKiSR http://dsg.cs.put.poznan.pl/student-club/
Koło Naukowe SKiSR Koło Naukowe SKiSR http://dsg.cs.put.poznan.pl/student-club/ konrad.siek@cs.put.edu.pl