简体   繁体   English

使用 pthread 库的线程间同步

[英]Synchronization between threads using pthread library

For example, we have 5 pieces of data.(assume we have a lot of space, different version of data will not overlap each others.)例如,我们有5条数据。(假设我们有很多空间,不同版本的数据不会相互重叠。)

DATA0, DATA1, DATA2, DATA3, DATA4. DATA0、DATA1、DATA2、DATA3、DATA4。

We have 3 threads(less than 5) working on those data.我们有 3 个线程(少于 5 个)处理这些数据。

Thread 1, working on DATA1 (version 0), has accessed some data from both DATA0(version 0) and DATA2(version 0), and create DATA1(version 1).线程 1,在 DATA1(版本 0)上工作,已经访问了来自 DATA0(版本 0)和 DATA2(版本 0)的一些数据,并创建了 DATA1(版本 1)。

Thread 2, working on DATA3 (version 0), has accessed some data from both DATA2(version 0) and DATA4(version 0), and create DATA3(version 1).处理 DATA3(版本 0)的线程 2 访问了来自 DATA2(版本 0)和 DATA4(版本 0)的一些数据,并创建了 DATA3(版本 1)。

Thread 3, working on DATA2 (version 0), has accessed some data from both DATA1(version 0) and DATA3(version 0), and create DATA2(version 1).线程 3 处理 DATA2(版本 0),已经访问了来自 DATA1(版本 0)和 DATA3(版本 0)的一些数据,并创建了 DATA2(版本 1)。

Now, if thread 1 finishes first.现在,如果线程 1 先完成。 It has several choices, it can work on DATA0 (to create DATA0 version 1) since DATA1(version 0) and DATA4 (version 0) is available (Assume DATA0 & DATA4 are neighbors).它有多种选择,它可以在 DATA0(创建 DATA0 版本 1)上工作,因为 DATA1(版本 0)和 DATA4(版本 0)可用(假设 DATA0 和 DATA4 是邻居)。 It can also work on DATA 2 if it finds out that both DATA1(version1) and DATA3(version1) are available and create DATA2(version 2).如果它发现 DATA1(version1) 和 DATA3(version1) 都可用并创建 DATA2(version 2),它也可以在 DATA 2 上工作。

The requirement is the next version of data can be processed once it's neighbor data is ready(in 1 lower version).要求是一旦它的邻居数据准备好(在 1 个较低版本中),就可以处理下一版本的数据。

At last, I want all threads to exit when all data arrive at version 10.最后,当所有数据到达版本 10 时,我希望所有线程退出。

Question: How to implement this scheme using pthread library.问题:如何使用 pthread 库实现该方案。

Note: I want to have data in different versions at the same time, so to create a barrier and make sure all data reach the same version is not an option.注意:我想同时拥有不同版本的数据,因此创建障碍并确保所有数据都到达相同版本不是一种选择。

Lets discuss the implementation.让我们讨论一下实现。 To have all versions (0~10) stored we would need 5*11*sizeof(data) space.要存储所有版本 (0~10),我们需要 5*11*sizeof(data) 空间。 Let us create two arrays of size 5 x 11. First array is DATA such that DATA[i][j] is the j th version of data i.让我们创建两个大小为 5 x 11 的数组。第一个数组是 DATA,这样 DATA[i][j] 是数据 i 的第 j 个版本。 Second array is an 'Access Matrix' - A, it denotes the state of an index, it could be:第二个数组是一个“访问矩阵”-A,它表示索引的状态,它可以是:

  • Not started没有开始
  • In Progress进行中
  • Completed完全的

Algorithm: Each thread would search for an index [i][j] in the matrix such that, index [i-1][j-1] and [i+1][j-1] is 'Completed'.算法:每个线程将在矩阵中搜索索引 [i][j],使得索引 [i-1][j-1] 和 [i+1][j-1] 为“已完成”。 It would set A[i][j] to 'In Progress' while working on it.它会在处理它时将 A[i][j] 设置为“进行中”。 In case i=0, i-1 refers to n-1, if i=n-1, i+1 refers to 0. (like a circular queue).如果 i=0,i-1 指的是 n-1,如果 i=n-1,i+1 指的是 0。(就像一个循环队列)。 When all entries in the last column are 'Completed', the thread terminates.当最后一列中的所有条目都“已完成”时,线程终止。 Otherwise it searches for a new data which is not completed.否则,它将搜索未完成的新数据。

Using pthread library to realize this:使用 pthread 库来实现这一点:

Important variables: mutex, conditional variables.重要变量:互斥锁、条件变量。

pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t condvar= PTHREAD_COND_INITIALIZER; pthread_cond_t condvar= PTHREAD_COND_INITIALIZER;

mutex is a 'lock'.互斥锁是一个“锁”。 We use it when we need to make an operation atomic .我们在需要进行原子操作时使用它。 Atomic operation refers to an operation that needs to be done in 1 step without breaking execution.原子操作是指在不中断执行的情况下需要一步完成的操作。 'condvar' is a condition variable. 'condvar' 是一个条件变量。 Using it a thread can sleep until a condition is reached, when it is reached, the thread is woken up.使用它,线程可以休眠直到达到条件,当达到条件时,线程被唤醒。 This avoids busy waiting using a loop.这避免了使用循环的忙等待

Here, our atomic operation is updating A. Reason: If the threads simultaneously update A, it may lead to race conditions such as more than 1 thread working on a Data in parallel.这里,我们的原子操作是更新 A。 原因:如果线程同时更新 A,可能会导致竞争条件,例如多个线程并行处理一个 Data。

To realize this, we search and set A inside the lock.为了实现这一点,我们在锁搜索并设置 A。 Once A is set, we release the lock and work on the data.一旦设置了 A,我们就释放锁并处理数据。 But if no available data was found which could be worked on, we wait on the conditional variable - condvar .但是如果没有找到可以处理的可用数据,我们会等待条件变量 - condvar When we call wait on condvar, we also pass mutex.当我们在 condvar 上调用 wait 时,我们也会传递互斥锁。 While inside the lock, wait function releases the mutex lock and waits for the conditional variable to be signaled.在锁内部时,wait 函数释放互斥锁并等待条件变量发出信号。 Once it is signaled, it requires the lock and proceeds with execution.一旦发出信号,它就需要锁定并继续执行。 While waiting process is in sleeping state and hence does not waste CPU time.等待进程处于睡眠状态,因此不会浪费 CPU 时间。

Whenever any thread finishes working on a piece of data, it may prepare 1 or more other samples for being worked on.每当任何线程完成对一段数据的处理时,它可能会准备 1 个或多个其他样本以供处理。 Hence after a thread finishes work, it signals all other threads to check for a 'workable' Data before continuing the algorithm.因此,在线程完成工作后,它会通知所有其他线程在继续算法之前检查“可行”数据。 Pseudo code for this is as follows:其伪代码如下:

Read the comments and function names.阅读注释和函数名称。 They describe in detail the working of pthread library.他们详细描述了 pthread 库的工作。 While compilation with gcc add -lpthread flag and for further details of the library looking up the man pages of these functions is more than sufficient.虽然使用 gcc 编译添加 -lpthread 标志以及查找这些函数的手册页的库的更多详细信息已绰绰有余。

void thread(void)
{
    //Note: If there are various threads in the line pthread_mutex_lock(&mutex)
    // each will wait till the lock is released and acquired. Until then it will sleep.
    pthread_mutex_lock(&mutex); //Do the searching inside the lock
    while(lastColumnNotDone){   //This will avoid previously searched indices being updated
    //Search for a workable index
        if(found)
        {   //As A has been updated and set to in progress, no need to hold lock. As we start work on the thread we release the lock so other process might use it.
            pthread_mutex_unlock(&mutex); //Note:
            //WORK ON DATA
            pthread_mutex_lock(&mutex); //Restore lock to in order to continue thread's execution safely.
            pthread_cond_broadcast(&condvar); //Sends a wake up signal to all threads which are waiting for the conditional variable 'condvar'.
        }
        else //No executable data found
            pthread_cond_wait(&condvar,&mutex); //While waiting, we pass the address of mutex as second parameter to wait function. This releases the lock on mutex while this function is waiting and tries to reacquire it once condvar is signaled.
    }
    pthread_mutex_unlock(&mutex);
}

Search and checking if all data is completed in the while loop condition can be optimized but that is a different algorithms question.可以优化在 while 循环条件中搜索和检查所有数据是否完成,但这是一个不同的算法问题。 Key idea here is use of pthread library and thread concept.这里的关键思想是使用 pthread 库和线程概念。

  • A is a common access matrix. A 是公共访问矩阵。 Do NOT update it outside of lock.不要在锁定之外更新它。
  • While checking anything with respect to A, such as finding a process or checking if all data is done, lock must be held.在检查与 A 相关的任何事情时,例如查找进程或检查所有数据是否已完成,必须持有锁。 Otherwise A can be changed by a different thread at the same time a thread is reading it.否则 A 可以在一个线程正在读取它的同时被另一个线程更改。
  • We acquire and release locks using the functions pthread_mutex_lock and pthread_mutex_unlock.我们使用函数 pthread_mutex_lock 和 pthread_mutex_unlock 获取和释放锁。 Remember, these functions take pointers of the mutex and not it's value.请记住,这些函数采用互斥锁的指针而不是它的值。 It is a variable that needs to be accessed and updated.它是一个需要访问和更新的变量。
    • Avoid holding the lock for long amounts of time.避免长时间持有锁。 This will cause the threads to wait for a long time for small access needs.这将导致线程等待很长时间来满足小的访问需求。
    • When calling wait, be sure that lock is held.调用 wait 时,请确保持有锁。 Wait unlocks the mutex held passed as the second parameter during the duration of it's wait.等待解锁在等待期间作为第二个参数传递的互斥锁。 After receiving the signal to wake up it tries to acquire the lock once again.收到唤醒信号后,它会再次尝试获取锁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM