[CA-2]pthread详细介绍

前言

与OpenMP不同,pthread可以进行一些更精细的操作,比如中途退出线程等等

pthread_create

顾名思义,这个函数就是创建一个线程,其FORMAT为

#define _OPEN_THREADS
#include <pthread.h>

int pthread_create(pthread_t *thread, pthread_attr_t *attr,
                   void *(*start_routine) (void *arg), void *arg);

attr可以是一个struct的指针,里面包含有所有你需要赋给这个线程的参数,如果attr为NULL,那么使用默认值
如果创建成功,函数返回0,否则返回-1,并且返回报错类型
EAGAIN:线程资源不足,不能创建下一个线程
EINVAL:第一个参数即thread为null
ELEMULTITHREADFORK:
pthread_create() was invoked from a child process created by calling fork() from a multi-threaded process. This child process is restricted from becoming multi-threaded.
ENOMEM:内存不够创建线程
例子(来自官方文档):

/* CELEBP27 */
#define _OPEN_THREADS
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
void *thread(void *arg) {
  char *ret;
  printf("thread() entered with argument '%s'\n", arg);
  if ((ret = (char*) malloc(20)) == NULL) {
    perror("malloc() error");
    exit(2);
  }
  strcpy(ret, "This is a test");
  pthread_exit(ret);
}

main() {
  pthread_t thid;
  void *ret;

  if (pthread_create(&thid, NULL, thread, "thread 1") != 0) {
    perror("pthread_create() error");
    exit(1);
  }

  if (pthread_join(thid, &ret) != 0) {
    perror("pthread_create() error");
    exit(3);
  }

  printf("thread exited with '%s'\n", ret);
}

输出:

thread() entered with argument 'thread 1'
thread exited with 'This is a test'

pthread_join

这个函数的作用是等待一个线程的结束,其FORMAT为

#define _OPEN_THREADS
#include <pthread.h>

int pthread_join(pthread_t thread, void **status);

pthread_t这种参数类型是为了识别线程号的,每个线程在创建时都有唯一的线程号,有了这个就可以选中特定的线程
status contains a pointer to the status argument passed by the ending thread as part of pthread_exit(). If the ending thread terminated with a return, status contains a pointer to the return value. If the thread was canceled, status can be set to -1.
如果线程结束成功,返回0,否则返回-1并且返回报错信息
EDEADLK:目标直接或间接地加入到了线程中
EINVAL:thread的值不合法
ESRCH:thread并不是一个非分离的线程

pthread_detach

#define _OPEN_THREADS
#include <pthread.h>

int pthread_detach(pthread_t *thread);

起作用是允许存储其线程 ID 位于位置线程中的线程,以便在该线程结束时进行回收。无论螺纹是否分离,此存储都是在进程退出时回收的,并且可能包括用于线程返回值的存储。如果线程尚未结束,pthread_detach()不会导致其结束。
就不需要最后join了
如果线程结束成功,返回0,否则返回-1并且返回报错信息
EINVAL:thread的值不合法
ESRCH:thread是一个已经分离的线程

Makefile

# Specify the compiler and flags that will be used.
CC=gcc
CFLAGS=-Wpedantic -Wall -Wextra -Werror 

# Simplify the target list below.

SHA256_HEADER=hash_functions/sha256.h
BLOCKCHAIN_HEADER=blockchain.h hash_function.h bool.h

SHA256_IMPL=hash_functions/sha256.c
BLOCKCHAIN_IMPL=blockchain.c hash_function.c

SHA256_TEST=hash_functions/sha256_test.c
BLOCKCHAIN_TEST=test.c

SHA256_TARGET=test-sha256.out
BLOCKCHAIN_TARGET=blockchain.out

SUBMISSION_IMPL=Makefile blockchain.c
SUBMISSION_TARGET=hw5.tar

# Target for the blockchain implementation that will test your implementation.
# Trigger this target by `make` or `make blockchain.out`.
$(BLOCKCHAIN_TARGET): Makefile $(SHA256_HEADER) $(BLOCKCHAIN_HEADER) $(SHA256_IMPL) $(BLOCKCHAIN_IMPL) $(BLOCKCHAIN_TEST)
    ${CC} ${CFLAGS} $(BLOCKCHAIN_IMPL) $(SHA256_IMPL) $(BLOCKCHAIN_TEST) -lpthread -o $(BLOCKCHAIN_TARGET)

# Target for testing if the SHA256 implementation works on your computer.
# Trigger this target by `make test-sha256.out`.
$(SHA256_TARGET): Makefile $(SHA256_TEST) $(SHA256_IMPL) $(SHA256_HEADER)
    ${CC} ${CFLAGS} $(SHA256_IMPL) $(SHA256_TEST) -lpthread -o $(SHA256_TARGET)

# Target for creating the tarball for submission.
# Trigger this target by `make submission`.
.PHONY: submission
submission:
    tar -cvpf $(SUBMISSION_TARGET) $(SUBMISSION_IMPL)
    
# Target for cleanup your workspace - deleting all files created by your compiler.
# Trigger this target by `make clean`.
.PHONY: clean
clean:
    rm -rf $(BLOCKCHAIN_TARGET) $(SHA256_TARGET) $(SUBMISSION_TARGET) *.dSYM

头文件

#ifndef _BLOCKCHAIN_H
#define _BLOCKCHAIN_H

#include "bool.h"
#include "hash_function.h"
#include <stddef.h>
#include <stdint.h>

struct blockchain_node_header {
  uint32_t index;
  uint32_t timestamp;
  unsigned char prev_hash[HASH_BLOCK_SIZE];
  unsigned char data[256];
  uint64_t nonce;
} __attribute__((__packed__));

typedef struct blockchain_node_header blkh_t;

struct blockchain_node {
  blkh_t header;
  unsigned char hash[HASH_BLOCK_SIZE];
} __attribute__((__packed__));

typedef struct blockchain_node blk_t;

/*
  Initalize your block with index, timestamp, hash from previous block and a set
  of custom data.

  You are not supposed to calcutale hash nonce (i.e., mining) in this function.
  if the data is less than 256 bytes long, padding all the rest bytes with 0. If
  it is more than 256 bytes long, truncate it to 256 bytes.
 */
void blockchain_node_init(blk_t *node, uint32_t index, uint32_t timestamp,
                          unsigned char prev_hash[HASH_BLOCK_SIZE],
                          unsigned char *data, size_t data_size);

/*
  Calculate hash from header of the node (which could be done with smart use of
  `struct blockchain_node_header`), store the hash to the `hash_buf` argument.
*/
void blockchain_node_hash(blk_t *node, unsigned char hash_buf[HASH_BLOCK_SIZE],
                          hash_func func);

/*
  Verify if `node` is legitimate (i.e., have vaild hash) and if `node`'s
  previous node is `prev_node`.
*/
BOOL blockchain_node_verify(blk_t *node, blk_t *prev_node, hash_func func);

/*
  Do mining. The first 'diff' bits of hash should be 0. Only the execution of
  the function will be counted to the mining time of your program
*/
void blockchain_node_mine(blk_t *node, unsigned char hash_buf[HASH_BLOCK_SIZE],
                          size_t diff, hash_func func);

#endif /* blockchain.h */

原文件(为使用pthread加速的文件)

#include "blockchain.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define THREAD_NUM 5




void blockchain_node_init(blk_t *node, uint32_t index, uint32_t timestamp,
                          unsigned char prev_hash[32], unsigned char *data,
                          size_t data_size) {
    if (!node || !data || !prev_hash) // node data prev_hash valid 
    return;

  node->header.index = index;
  node->header.timestamp = timestamp;
  node->header.nonce = -1;

  memset(node->header.data, 0, sizeof(unsigned char) * 256);
  memcpy(node->header.prev_hash, prev_hash, HASH_BLOCK_SIZE);
  memcpy(node->header.data, data,
         sizeof(unsigned char) * ((data_size < 256) ? data_size : 256));
}

void blockchain_node_hash(blk_t *node, unsigned char hash_buf[HASH_BLOCK_SIZE],
                          hash_func func) {
  if (node)
    func((unsigned char *)node, sizeof(blkh_t), (unsigned char *)hash_buf);
}

BOOL blockchain_node_verify(blk_t *node, blk_t *prev_node, hash_func func) {
  unsigned char hash_buf[HASH_BLOCK_SIZE];

  if (!node || !prev_node)
    return False;

  blockchain_node_hash(node, hash_buf, func);
  if (memcmp(node->hash, hash_buf, sizeof(unsigned char) * HASH_BLOCK_SIZE))
    return False;

  blockchain_node_hash(prev_node, hash_buf, func);
  if (memcmp(node->header.prev_hash, hash_buf,
             sizeof(unsigned char) * HASH_BLOCK_SIZE))
    return False;

  return True;
}

/* The sequiental implementation of mining implemented for you. */
void blockchain_node_mine(blk_t *node, unsigned char hash_buf[HASH_BLOCK_SIZE],
                          size_t diff, hash_func func) {
  unsigned char one_diff[HASH_BLOCK_SIZE];
  size_t diff_q, diff_m;
  diff_q = diff / 8;
  diff_m = diff % 8;
  memset(one_diff, 0xFF, sizeof(unsigned char) * HASH_BLOCK_SIZE);
  memset(one_diff, 0, sizeof(unsigned char) * diff_q);
  one_diff[diff_q] = ((uint8_t)0xFF) >> diff_m;
  
}

加速后的文件

#include "blockchain.h"
#include <stdlib.h>
#include <string.h>
#include  <pthread.h>

#define THREAD_NUM 20
int nounce = 0;

void blockchain_node_init(blk_t *node, uint32_t index, uint32_t timestamp,
                          unsigned char prev_hash[32], unsigned char *data,
                          size_t data_size) {
    if (!node || !data || !prev_hash)
        return;

    node->header.index = index;
    node->header.timestamp = timestamp;
    node->header.nonce = -1;

    memset(node->header.data, 0, sizeof(unsigned char) * 256);
    memcpy(node->header.prev_hash, prev_hash, HASH_BLOCK_SIZE);
    memcpy(node->header.data, data,
           sizeof(unsigned char) * ((data_size < 256) ? data_size : 256));
}

void blockchain_node_hash(blk_t *node, unsigned char hash_buf[HASH_BLOCK_SIZE],
                          hash_func func) {
    if (node)
        func((unsigned char *) node, sizeof(blkh_t), (unsigned char *) hash_buf);
}

BOOL blockchain_node_verify(blk_t *node, blk_t *prev_node, hash_func func) {
    unsigned char hash_buf[HASH_BLOCK_SIZE];

    if (!node || !prev_node)
        return False;

    blockchain_node_hash(node, hash_buf, func);
    if (memcmp(node->hash, hash_buf, sizeof(unsigned char) * HASH_BLOCK_SIZE))
        return False;

    blockchain_node_hash(prev_node, hash_buf, func);
    if (memcmp(node->header.prev_hash, hash_buf,
               sizeof(unsigned char) * HASH_BLOCK_SIZE))
        return False;

    return True;
}

struct mydata {
    blk_t *node;
    hash_func *func;
    unsigned char *hash_buf;
    unsigned char *one_diff;
    size_t diff_q;
    uint64_t nonce;
    pthread_mutex_t mutex;
    int *ops;
    int flag;
};

void *thread_in_while(void *mydata) {
    struct mydata *my_data = (struct mydata *) mydata;
    size_t diff_q;
    int j;
    blk_t temp_node;
    unsigned char hash_buf[HASH_BLOCK_SIZE];
    unsigned char *one_diff;
    uint64_t buffer_1;
    uint64_t buffer_2;
    unsigned char *str1;
    unsigned char *str2;

    memcpy(hash_buf, my_data->hash_buf, sizeof(unsigned char) * HASH_BLOCK_SIZE);
    memcpy(&temp_node, my_data->node, sizeof(blk_t));
    diff_q = my_data->diff_q ;
    buffer_1 = sizeof(unsigned char) * (HASH_BLOCK_SIZE - diff_q);
    buffer_2 = sizeof(unsigned char) * diff_q;
    one_diff = my_data->one_diff;
    str1 = &(hash_buf[diff_q]);
    str2 = &(one_diff[diff_q]);

    pthread_mutex_lock(&my_data->mutex);//
    for (j = 0; j < 20; j++) {
        if (my_data->ops[j] == 0) {
            my_data->ops[j] = 1;
            break;
        }
    }
    pthread_mutex_unlock(&my_data->mutex);
    temp_node.header.nonce += j;

    while (my_data->flag){ 
        blockchain_node_hash(&temp_node, hash_buf, my_data->func);
        if ((!memcmp(hash_buf, one_diff, buffer_2)) && memcmp(str1, str2, buffer_1) <= 0) {

            pthread_mutex_lock(&my_data->mutex);
            if (!my_data->flag)
                pthread_exit(NULL);
            my_data->flag = 0;
            nounce = temp_node.header.nonce;

            pthread_mutex_unlock(&my_data->mutex);
            memcpy(my_data->node->hash, hash_buf, sizeof(unsigned char) * HASH_BLOCK_SIZE);
            memcpy(my_data->hash_buf, hash_buf, sizeof(unsigned char) * HASH_BLOCK_SIZE);
            pthread_exit(NULL);
        }
        temp_node.header.nonce += THREAD_NUM;
        
    }
    return NULL;
}

/* The sequiental implementation of mining implemented for you. */
void blockchain_node_mine(blk_t *node, unsigned char hash_buf[HASH_BLOCK_SIZE],
                          size_t diff, hash_func func) {
    unsigned char one_diff[HASH_BLOCK_SIZE];
    size_t diff_q, diff_m;
    pthread_mutex_t mutex;
    int ops[THREAD_NUM] = {0};
    diff_q = diff / 8;
    diff_m = diff % 8;
    pthread_t thread[THREAD_NUM];
    int flag = 1;
    int i;

    memset(one_diff, 0xFF, sizeof(unsigned char) * HASH_BLOCK_SIZE);
    memset(one_diff, 0, sizeof(unsigned char) * diff_q);
    one_diff[diff_q] = ((uint8_t) 0xFF) >> diff_m;
    pthread_mutex_init(&mutex, NULL);

    struct mydata my_data;
    my_data.node = node;
    my_data.hash_buf = hash_buf;
    my_data.one_diff = one_diff;
    my_data.diff_q = diff_q;
    my_data.func = func;
    my_data.mutex = mutex;
    my_data.ops = ops;
    my_data.nonce = -1;
    my_data.flag = flag;
    for (i = 0; i < THREAD_NUM; i++) {
        pthread_create(&thread[i], NULL, thread_in_while, &my_data);
    }
    for (i = 0; i < THREAD_NUM; i++) {
        pthread_join(thread[i], NULL);
    }
    node->header.nonce = nounce;
}
}


解析

因为我为了节省空间所以将这些线程共用了资源,但是这样会导致一些问题,所以我加了线程锁,并且在哈希出的值符合时直接退出,节省了时间
在这里插入图片描述
pthread_mutex_lock与pthread_mutex_unlock合用有点像OpenMP的#pragema critical,只不过我们这里是访问元素的时候只能一个一个来,而OpenMP是执行那段代码的时候只能一个一个来

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值