gpt4 book ai didi

c - 使用互斥体时的数据竞争

转载 作者:行者123 更新时间:2023-11-30 17:54:02 26 4
gpt4 key购买 nike

我正在开发一个涉及线程和互斥体的 C 小型项目。我正在开发的程序对 bmp 图像应用过滤器。该项目的目标是实现一个能够处理此命令行的程序:

$ ./filter -f filter1[,filter2[,...]] -t numThreads1[,numThreads2[,...]] input-folder output-folder

其中 -f 是我要应用的过滤器(“红色”、“蓝色”、“绿色”、“灰度”和“模糊”),-t 是每个过滤器分配的线程数。

到目前为止,除了模糊之外,一切都很好,我陷入了数据竞争(或者,我认为是这样)。模糊滤镜的工作原理如下:

/* Add a Gaussian blur to an image using
* this 3X3 matrix as weights matrix:
* 0.0 0.2 0.0
* 0.2 0.2 0.2
* 0.0 0.2 0.0
*
* If we consider the red component in this image
* (every element has a value between 0 and 255)
*
* 1 2 5 2 0 3
* -------
* 3 |2 5 1| 6 0 0.0*2 + 0.2*5 + 0.0*1 +
* | |
* 4 |3 6 2| 1 4 -> 0.2*3 + 0.2*6 + 0.2*2 + -> 3.2
* | |
* 0 |4 0 3| 4 2 0.0*4 + 0.2*0 + 0.0*3
* -------
* 9 6 5 0 3 9
*
* The new value of the pixel (3, 4) is round(3.2) = 3.
*
* If a pixel is outside the image, we increment the central pixel weight by 0.2
* So the new value of pixel (0, 0) is:
* 0.2 * 0 + 0.2 * 9 + 0.2 * 6 + 0.2 * 9 + 0.2 * 9 = 6.6 -> 7
*/

问题是,当我使用这个模糊滤镜在“棋盘”图像上运行我的程序时:

$ ./filter -f blur -t 8 chess.bmp chessBlur.bmp

我期待得到这个image ,但我得到 this (“断”线随机变化)

我使用互斥锁来锁定和解锁关键部分,但正如您所看到的,数据争用仍然发生。在我的过滤器上只有两个字,我一次给每个线程一行,从底部开始向上。我的 filter_blur 代码是:

int filter_blur(struct image *img, int nThread)
{
int error = 0;
int mod = img->height%nThread;
if (mod > 0)
mod = 1;

pthread_t threads[nThread];
pthread_mutex_t mutex;
args arguments[nThread];

struct image* img2 = (struct image*)malloc(sizeof(struct image));
memcpy(img2,img,sizeof(struct image));

error=pthread_mutex_init( &mutex, NULL);
if(error!=0)
err(error,"pthread_mutex_init");

int i = 0;
for (i=0; i<nThread; i++) {
arguments[i].img2 = img2;
arguments[i].mutex = &mutex;
}

int j = 0;
for (i=0; i<(img->height)/nThread + mod; i++) {
for (j=0; j<nThread; j++) {

arguments[j].img = img; arguments[j].line = i*nThread + j;

error=pthread_create(&threads[j],NULL,threadBlur,(void*)&arguments[j]);
if(error!=0)
err(error,"pthread_create");
}
for (j=0; j<nThread; j++) {
error=pthread_join(threads[j],NULL);
if(error!=0)
err(error,"pthread_join");
}
}
free(img2);
return 0;
}

void* threadBlur(void* argument) {

// unpacking arguments
args* image = (args*)argument;
struct image* img = image->img;
struct image* img2 = image->img2;
pthread_mutex_t* mutex = image->mutex;

int error;
int line = image->line;
if (line < img->height) {
int i;

error=pthread_mutex_lock(mutex);
if(error!=0)
fprintf(stderr,"pthread_mutex_lock");

for (i=0; i<img->width; i++) {
img->pixels[line * img->width +i] = blur(img2,i,line);
}

error=pthread_mutex_unlock(mutex);
if(error!=0)
fprintf(stderr,"pthread_mutex_unlock");
}
pthread_exit(NULL);
}

struct pixel blur(struct image* img2, int x, int y) {
double red = 0;
double green = 0;
double blue = 0;

red=(double)img2->pixels[y * img2->width + x].r/5.0;
green=(double)img2->pixels[y * img2->width + x].g/5.0;
blue=(double)img2->pixels[y * img2->width + x].b/5.0;

if (x != 0) {
red+=(double)img2->pixels[y * img2->width + x - 1].r/5.0;
green+=(double)img2->pixels[y * img2->width + x - 1].g/5.0;
blue+=(double)img2->pixels[y * img2->width + x - 1].b/5.0;
} else {
red+=(double)img2->pixels[y * img2->width + x].r/5.0;
green+=(double)img2->pixels[y * img2->width + x].g/5.0;
blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
}

if (x != img2->width - 1) {
red+=(double)img2->pixels[y * img2->width + x + 1].r/5.0;
green+=(double)img2->pixels[y * img2->width + x + 1].g/5.0;
blue+=(double)img2->pixels[y * img2->width + x + 1].b/5.0;
} else {
red+=(double)img2->pixels[y * img2->width + x].r/5.0;
green+=(double)img2->pixels[y * img2->width + x].g/5.0;
blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
}

if (y != 0) {
red+=(double)img2->pixels[(y - 1) * img2->width + x].r/5.0;
green+=(double)img2->pixels[(y - 1) * img2->width + x].g/5.0;
blue+=(double)img2->pixels[(y - 1) * img2->width + x].b/5.0;
} else {
red+=(double)img2->pixels[y * img2->width + x].r/5.0;
green+=(double)img2->pixels[y * img2->width + x].g/5.0;
blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
}

if (y != img2->height - 1) {
red+=(double)img2->pixels[(y + 1) * img2->width + x].r/5.0;
green+=(double)img2->pixels[(y + 1) * img2->width + x].g/5.0;
blue+=(double)img2->pixels[(y + 1) * img2->width + x].b/5.0;
} else {
red+=(double)img2->pixels[y * img2->width + x].r/5.0;
green+=(double)img2->pixels[y * img2->width + x].g/5.0;
blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
}

struct pixel pix = {(unsigned char)round(blue),(unsigned char)round(green),(unsigned char)round(red)};
return pix;
}

编辑1:

正如@job正确猜测的那样,问题是由我的结构体的memcpy引起的(结构体被复制,但结构体内部的指针仍然指向原始结构体元素)。我还删除了互斥体(它们之所以在这里是因为我认为它们可以解决我的问题,抱歉,我的错)现在我的项目运行得非常顺利(即使我们仍然可以讨论处理速度以及使用线程的需要)。正如我所说,这是一个项目,是我 C 类的大学项目。目标是并行化我们的过滤器。所以需要线程。

谢谢!

最佳答案

好吧,这并不是一个答案,而是关于您的代码的一些观察结果:

  • 您似乎并没有真正从程序中任何位置的多个线程访问一个特定的内存单元。所以看起来 mutices 是不需要的。

  • 或者,也许线程确实访问相同的内存段。在这种情况下,您的程序很可能只需一个线程即可完成所有计算,效率会高得多。您应该对这种情况进行基准测试并将其与线程版本进行比较。

  • 至少对我来说,没有明显的理由说明为什么这里需要多线程。如果您在单个线程中进行这些浮点计算,它们可能会在操作系统设法生成第二个线程之前完成。与线程创建开销时间相比,工作负载微不足道。

  • 您当前的多线程设计存在缺陷,所有工作都在互斥锁保护的代码内进行。在互斥锁之外没有任何实际工作可以完成,因此无论您创建 1000 个线程,一次只能执行 1 个线程,其他线程将处于休眠状态等待轮到它们。

关于c - 使用互斥体时的数据竞争,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15292131/

26 4 0