Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multithreaded Resource Access - Where Do I Put My Locks?

I have threaded code where each thread needs to write to the same file. To prevent concurrency issues, I am using a Lock object.

My question is whether I am using the Lock correctly. If I set the lock from within each thread, is that lock global or only specific to that specific thread?

Basically, should I create a Lock first and pass its reference to each thread, or is it ok to set it from within the thread like I do here:

import time
from threading import Thread, Lock

def main():
    for i in range(20):
        agent = Agent(i)
        agent.start()

class Agent(Thread):
    def __init__(self, thread_num):
        Thread.__init__(self)
        self.thread_num = thread_num

    def run(self):
        while True:
            print 'hello from thread %s' % self.thread_num
            self.write_result()   

    def write_result(self):
        lock = Lock()
        lock.acquire()
        try:
            f = open('foo.txt', 'a')
            f.write('hello from thread %s\n' % self.thread_num)
            f.flush()
            f.close()
        finally:
            lock.release()

if __name__ == '__main__':
    main()
like image 213
Corey Goldberg Avatar asked Jan 15 '09 19:01

Corey Goldberg


1 Answers

For your use case one approach could be to write a file subclass that locks:

class LockedWrite(file):
    """ Wrapper class to a file object that locks writes """
    def __init__(self, *args, **kwds):
        super(LockedWrite, self).__init__(*args, **kwds)
        self._lock = Lock()

    def write(self, *args, **kwds):
        self._lock.acquire()
        try:
            super(LockedWrite, self).write(*args, **kwds)
        finally:
            self._lock.release()

To use in your code just replace following functions:

def main():
    f = LockedWrite('foo.txt', 'a')

    for i in range(20):
        agent = Agent(i, f)
        agent.start()

class Agent(Thread):
    def __init__(self, thread_num, fileobj):
        Thread.__init__(self)
        self.thread_num = thread_num
        self._file = fileobj    

#   ...

    def write_result(self):
        self._file.write('hello from thread %s\n' % self.thread_num)

This approach puts file locking in the file itself which seems cleaner IMHO

like image 98
nosklo Avatar answered Oct 11 '22 14:10

nosklo