Search code examples
phpmultithreadingtheory

Best Practice For Sleeping


Suppose you have a process that writes to a file and takes exactly 1 second (theoretical) to execute. You place a lock on the file to prevent writing while writing; however this question should apply to any race condition. Is it best to "Loop and Check" or "Wait For Signal" (such as pcntl_sigwaitinfo and pcntl_signal in PHP).

If "Loop and Check" is preferred, for best performance, how long should the process waiting to receive the lock go to sleep for, or in other words how often should the process wake up to check the status of the lock?

Assuming the CPU cycle time is 20 nanoseconds (ns):

If you wait 1 full second, the maximum wasted time is ( 1s - 1ns ) or almost an entire second assuming the previous process finished 1ns after the last check. However, checking too often is also wasteful. I'm thinking of using

 [ ( Execution Time / (4 * Execution Time) ]. 

This formula should reduce to check 4 times given the length of the execution. What do you guys think?


Solution

  • I can't really make sense out of what you are trying to do, exactly, so I'll make some assumptions.

    I'll assume that you are locking the file to preserve integrity of the contents rather than to create some kind of file based condition, because you don't need files for conditions.

    I'll assume that you have overlooked the fact that loop and check is a good way to introduce race conditions into anything, consider the following code

    if (can_lock_file())
        lock_file();
    

    If more than one context is executing, and I'm assuming it is due to the multi-threading tag on the question, then you are basically programming a deadlock - if two contexts call can_lock_file at the same time, they could both get a positive return value and both attempt to lock the file, blocking and or deadlocking one of those contexts, depending on code.

    The best advice is don't make those kinds of decisions in code, there is no good way to get it right.

    Here is some code that has 8 threads writing randomly generated data to a single output file, the integrity of that file is preserved in the proper way with mutex, the nature of the data and process will dictate exactly how real mutex are used, I took a best guess:

    <?php
    
    class FileWriter extends Thread {
        public function __construct($file, $lock) {
            $this->file = $file;
        }
    
        public function run() {
            /* the file does not need to be locked while data is generated */
            $data = [];
            while (count($data) < 1000) {
                $data[] = md5(mt_rand()*microtime());
            }
    
            $tid = $this->getThreadId();
    
            foreach ($data as $line) {
                /* the lock need only be held while the file is written */
                Mutex::lock($this->lock);
                fprintf(
                    $this->file,
                    "%s#%lu: %s\n", __CLASS__, $tid, $line);
                Mutex::unlock($this->lock);
            }
        }
    
        protected $file;
        protected $lock;
    }
    
    
    $threads = [];
    $thread = 0;
    
    $handle = fopen("test.txt", "w+");
    $lock = Mutex::create();
    
    while ($thread<8) {
        $threads[$thread] = new FileWriter($handle, $lock);
        $threads[$thread]->start();
        $thread++;
    }
    
    foreach ($threads as $thread)
        $thread->join();
    
    
    Mutex::destroy($lock);
    ?>