The university was given a task using threads / processes and one of the tasks is that when two threads access the same file, an error should occur, saying that it is impossible to get access to the same file by two threads (this error is solved by adding Rlock). Since GIL "dwells" in Python, it was decided to use the multiprocessing module, and how to implement it ... is unclear.

def worker1(word): f = open("file.txt", 'w') f.write(word) f.close() def worker2(word): f = open("file.txt", 'w') f.write(word) f.close() multiprocessing.Process(target=worker1('worker1'),).start() multiprocessing.Process(target=worker2('worker2'),).start() 


I ask for your help.

  • one
    Your code does not start threads. And why you mentioned GIL is not clear. - Sergey Gornostaev
  • @SergeyGornostaev, I tended to the fact that with GIL you can not get an error with access. And how then to make it run? In the question under the threads, I meant the processes, but if it can be implemented by threads from the created process, then tell me, please, how to do it - JackWolf
  • at least you haven't even started the processes. to start, you need to run the start() method - Mr Morgan
  • @MrMorgan, yes, yes, I forgot to write them in, thank you, but still, I'm doing something wrong, if you know how to do it properly , please tell us - JackWolf
  • @JackWolf with GIL can get an error with access, since during the execution of system tasks like reading a file, this same GIL is released and allows other threads to work - andreymal

1 answer 1

Windows

That such a demo should be sufficient. The file opens with the 0 flag in dwShareMode , which means

If you request it, delete, read, or write access.

 import multiprocessing import time import ctypes from ctypes.wintypes import DWORD CREATE_ALWAYS = 2 GENERIC_WRITE = 0x40000000 def worker(word,i): print('Started {}'.format(i)) try: handle = ctypes.windll.kernel32.CreateFileA("file.txt", GENERIC_WRITE, 0, None, CREATE_ALWAYS, 0, None ) if handle == -1: # ctypes.WinError sets up an GetLastError API call for windows as an Python OSError exception. # So we use this to raise the error to our caller. raise ctypes.WinError() print('Process {}: File opened successfully'.format(i)) time.sleep(2) written = DWORD() ctypes.windll.kernel32.WriteFile(handle, word, len(word), ctypes.byref(written), None) print('Process {}: Write completed'.format(i)) ctypes.windll.kernel32.CloseHandle(handle) except Exception as e: print('Process {}: Cannot access file'.format(i)) print('{}: {}'.format(type(e).__name__,e.args)) print('Finished {}'.format(i)) if __name__ == '__main__': for i in range(2): multiprocessing.Process(target=worker, args=('worker{}'.format(i),i)).start() 

Displays:

 Started 0 Process 0: File opened successfully Started 1 Process 1: Cannot access file PermissionError: (13, 'The process cannot access the file because it is being used by another process.', None, 32) Finished 1 Process 0: Write completed Finished 0 

Posix

Now there is nothing at hand to test, but it should work. :)

Here you have to explicitly lock the file after opening. And since this is a blocking operation, we do a timeout using the signal module and the corresponding binding.

 import multiprocessing import time import signal, errno from contextlib import contextmanager import fcntl @contextmanager def timeout(seconds): def timeout_handler(signum, frame): pass original_handler = signal.signal(signal.SIGALRM, timeout_handler) try: signal.alarm(seconds) yield finally: signal.alarm(0) signal.signal(signal.SIGALRM, original_handler) def worker(word,i): print('Started {}'.format(i)) with timeout(1): try: f = open("file.txt", "w") print('Process {}: File opened successfully'.format(i)) fcntl.flock(f.fileno(), fcntl.LOCK_EX) print('Process {}: File locked successfully'.format(i)) time.sleep(3) f.write(word) print('Process {}: Write completed'.format(i)) fcntl.flock(f.fileno(), fcntl.LOCK_UN) print('Process {}: File unlocked'.format(i)) f.close() except IOError as e: if e.errno != errno.EINTR: raise e print('Process {}: Cannot access file'.format(i)) print('Finished {}'.format(i)) if __name__ == '__main__': for i in range(2): multiprocessing.Process(target=worker, args=('worker{}'.format(i),i)).start() 
  • Wow, thanks a lot. Tell me, please, is there any equivalent of ctypes.wintypes for unix (I just have MacOS), and I don’t really want to install Windows? - JackWolf
  • @JackWolf Why didn't you say it right away? As Sergey Gornostaev pointed out, the demo will be platform-specific. Now I will look about posix - Sergey Nudnov
  • I forgot something) Please take a look, if it’s easy, especially thanks to your answer I learned about the new library - JackWolf
  • Once again, thank you very much! - JackWolf
  • everything works, but at the end there is an error "ValueError: I / O operation on closed file", how to solve it? - JackWolf