POSIX多线程程序设计(第4章:使用线程的几种方式)

2023-11-11

1概述

线程编程模型有以下三种方式:

模型

说明

流水线

每个线程反复地在数据系列集上执行同一种操作,并把操作结果传递给下一步骤的其他线程,这就是“流水线”方式。即流水线上的线程对数据执行同一种操作,如简单的加1,再把数据的操作结果传递给下一个线程,直到流水线上的最后一个线程。

工作组

每个线程在自己的数据上执行操作,工作组中的线程可能执行相同或不同的操作,但一定独立执行。即线程作为工作组的成员,为完成一项工作而分工进行。如查找一个目录下包含某个字符串的文件有几个,可以让工作组的每个成员线程分别去查一个文件。

客户端/服务器

一个客户为每一件工作向一个独立的服务器提交请求(如读或写),由服务器来处理这些(如读或写)请求。

2流水线


#include "errors.h"
#include <pthread.h>
#include <sys/prctl.h>

/*线程编程模型:  流水线模型*/

/*流水线成员线程处理的数据*/
typedef struct stage_tag
{
    pthread_mutex_t  mutex;/*线程数据互斥量*/
    pthread_cond_t   avail;/*线程是否可用的条件变量*/
    pthread_cond_t   ready;
    pthread_t       thread;/*线程ID*/
    int         data_ready;/*1:表示数据已准备好;0:表示数据处理完成或未准备好*/
    long              data;/*线程数据*/
    struct stage_tag *next;
}stage_t;


/*数据集系列*/
typedef struct pipe_tag
{
    pthread_mutex_t  mutex;/*线程链表互斥量*/
    stage_t          *head;/*指向流水线第一个线程要处理的数据*/
    stage_t          *tail;/*指向最后一个数据,即最终的数据处理结果*/
    int             stages;/*线程总的个数*/
    int             active;/*流水线上处理的数据的个数*/
}pipe_t;


int pipe_send(stage_t *stage, long data)
{
    int status;

    pthread_mutex_lock(&stage->mutex);

#if 1 /*线程上一个数据还未处理完成时,需要等待*/
    while (stage->data_ready)
    {
        status = pthread_cond_wait(&stage->ready, &stage->mutex);
        if (status != 0)
        {
            pthread_mutex_unlock(&stage->mutex);
            return status;
        }
    }
#endif
    stage->data = data;
    stage->data_ready = 1;
    /*数据已准备好,通知等待的线程开始处理数据*/
    status = pthread_cond_signal(&stage->avail);
    if (status != 0)
    {
        pthread_mutex_unlock(&stage->mutex);
        return status;
    }
   
    status = pthread_mutex_unlock(&stage->mutex);

    return status;
}


void *pipe_stage(void *arg)
{
    int status;
    stage_t *stage = (stage_t *)arg;
    stage_t *next_stage = stage->next;

    /*线程命名*/
    #if 0
    static int i = 0;
    char thread_name[20];
    sprintf(thread_name, "thread_%d", i++);
    prctl(PR_SET_NAME, ( unsigned long )thread_name); 
    #endif
    status = pthread_mutex_lock(&stage->mutex);
    if (status != 0)
    {
        err_abort(status, "lock pipe stage");
    }

    while (1)
    {
        while (stage->data_ready != 1)
        {
            status = pthread_cond_wait(&stage->avail, &stage->mutex);
            if (status != 0)
            {
                err_abort(status, "wait for previous stage");
            }
            /*将数据发送给下一个线程处理*/
            pipe_send(next_stage, stage->data + 1);
            stage->data_ready = 0;
#if 1       /*线程对数据的处理已经完成,通知等待的线程*/    
            status = pthread_cond_signal(&stage->ready);
            if (status != 0)
            {
                err_abort(status, "wake next stage");
            }
#endif
        }
    }
}

int pipe_create(pipe_t *pipe, int stages)
{
    int status;
    int pipe_index;
    stage_t *new_stage, *stage;
    stage_t **link = &pipe->head; 

    status = pthread_mutex_init(&pipe->mutex, NULL);
    if (status != 0)
    {
        err_abort(status, "init pipe mutex");
    }

    pipe->stages = stages;/*线程个数*/
    pipe->active = 0;

    /*生成线程处理的数据集系列,多创建一个额外的数据stage*/
    for (pipe_index = 0; pipe_index <= stages; pipe_index++)
    {
        new_stage = (stage_t *)malloc(sizeof(stage_t));
        if (new_stage == NULL)
        {
            errno_abort("allocate stage");
        }
        status = pthread_mutex_init(&new_stage->mutex, NULL);
        if (status != 0)
        {
            err_abort(status, "init stage mutex");
        }

        status = pthread_cond_init(&new_stage->avail, NULL);
        if (status != 0)
        {
            err_abort(status, "init avail cond");
        }
    
        status = pthread_cond_init(&new_stage->ready, NULL);
        if (status != 0)
        {
            err_abort(status, "init ready cond");
        }

        new_stage->data_ready = 0;
        *link = new_stage;
        link = &new_stage->next;
    }

    *link = (stage_t *)NULL;
    pipe->tail = new_stage;/*将第11个stage赋值给pipe的tail*/

    /*创建10个线程,第10个线程还处理了最后的stage*/
    for (stage = pipe->head; stage->next != NULL; stage = stage->next)
    {
        status = pthread_create(&stage->thread, NULL, pipe_stage, (void *)stage);
        if (status != 0)
        {
            err_abort(status, "create pipe stage");
        }
    }

    return 0;
}


int pipe_start(pipe_t *pipe, long value)
{
    int status;

    pthread_mutex_lock(&pipe->mutex);
    pipe->active++;/*流水线处理了几个数据*/
    pthread_mutex_unlock(&pipe->mutex);

    pipe_send(pipe->head, value);

    return 0;
}

int pipe_result(pipe_t *pipe, long *result)
{
    stage_t *tail = pipe->tail;
    long value;
    int empty = 0;
    int status;
    pthread_mutex_lock(&pipe->mutex);
    
    if (pipe->active <= 0)
        empty = 1;
    else
        pipe->active--;

    pthread_mutex_unlock(&pipe->mutex);

    /*流水线上为空则直接退出*/
    if (empty)
        return 0;

    pthread_mutex_lock(&tail->mutex);
    /*等待最后一个数据stage处理完*/
    while (!tail->data_ready)
        pthread_cond_wait(&tail->avail, &tail->mutex);

    *result = tail->data;
    tail->data_ready = 0;
    pthread_cond_signal(&tail->ready);

    pthread_mutex_unlock(&tail->mutex);
    
    return 1;
}

int main(int argc, char *argv[])
{
    pipe_t my_pipe;
    long value, result;

    int status;
    char line[128];

    pipe_create(&my_pipe, 10);
    printf("Enter integer values, or \"=\" for next result\n");

    while (1)
    {
        printf("Data> ");
        if (fgets(line, sizeof(line), stdin) == NULL)
            exit(0);

        if (strlen(line) <= 1)
            continue;

        if (strlen(line) <= 2 && line[0] == '=')
        {
            if (pipe_result(&my_pipe, &result))
                printf("Result is %ld\n", result);
            else
                printf("Pipe is empty\n");
        }
        else
        {
            if (sscanf(line, "%ld", &value) < 1)
                fprintf(stderr, "Enter an integer value\n");
            else
                pipe_start(&my_pipe, value);
        }
    }
    
    return 0;
}

3工作组


/*工作组模型*/
#include <sys/types.h>
#include <pthread.h>
#include <sys/stat.h>
#include <dirent.h>
#include "errors.h"

#define CREW_SIZE 4

typedef struct work_tag
{
    struct work_tag *next;
    char            *path;
    char          *string;
}work_t, *work_p;

typedef struct worker_tag
{
    int index;
    pthread_t thread;
    struct crew_tag *crew;
}worker_t, *worker_p;

typedef struct crew_tag
{
    int crew_size;
    worker_t crew[CREW_SIZE];
    long work_count;
    work_t *first, *last;
    pthread_mutex_t mutex;
    pthread_cond_t  done;
    pthread_cond_t  go;
}crew_t, *crew_p;

size_t path_max;
size_t name_max;


void *worker_routine(void *arg)
{
    worker_p mine = (worker_t *)arg;
    crew_p crew = mine->crew;
    work_p work,new_work;
    struct stat filestat;
    struct dirent *entry;
    int status;

    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max);
    if (entry == NULL)
        errno_abort("allocating dirent");

    pthread_mutex_lock(&crew->mutex);

    /*队列中没有work,需要等待*/
    while (crew->work_count == 0)
    {
        pthread_cond_wait(&crew->go, &crew->mutex);
    }
    pthread_mutex_unlock(&crew->mutex);

    while (1)
    {
        pthread_mutex_lock(&crew->mutex);

        /*没有work,需要等待*/
        while (crew->first == NULL)
        {
            pthread_cond_wait(&crew->go, &crew->mutex);
        }

        work = crew->first;
        crew->first = work->next;
        if (crew->first == NULL)
        {
            crew->last = NULL;
        }

        pthread_mutex_unlock(&crew->mutex);

        /*降序遍历目录层次结构*/
        status = lstat(work->path, &filestat);

        /*符号链接*/
        if (S_ISLNK(filestat.st_mode))
        {
            printf("thread %d: %s is a link, skipping.\n", mine->index, work->path);
        }
        /*目录文件*/
        else if (S_ISDIR(filestat.st_mode))
        {
            DIR *dir;
            struct dirent *result;

            /*打开目录*/
            dir = opendir(work->path);
            if (dir == NULL)
            {
                fprintf(stderr, "Unable to open directory %s: %d (%s)\n", work->path, errno, strerror(errno));
                continue;
            }

            while (1)
            {
                /*readdir_r线程安全函数,result返回目录项指针,entry返回下一个目录项*/
                status = readdir_r (dir, entry, &result);
                if (status != 0) 
                {
                    fprintf (stderr,"Unable to read directory %s: %d (%s)\n", work->path, status, strerror (status));
                    break;
                }
                if (result == NULL)
                    break;

                if (strcmp (entry->d_name, ".") == 0)
                    continue;
                if (strcmp (entry->d_name, "..") == 0)
                    continue;
                new_work = (work_p)malloc(sizeof (work_t));
                if (new_work == NULL)
                    errno_abort ("Unable to allocate space");
                new_work->path = (char*)malloc(path_max);
                if (new_work->path == NULL)
                    errno_abort ("Unable to allocate path");

                strcpy (new_work->path, work->path);                
                strcat (new_work->path, "/");                
                strcat (new_work->path, entry->d_name);                
                new_work->string = work->string;                
                new_work->next = NULL;

                status = pthread_mutex_lock (&crew->mutex);                
                if (status != 0)                    
                    err_abort (status, "Lock mutex");    

                /*将new_work插入first队列的尾端,并将last指向最后一个new_work*/
                if (crew->first == NULL) 
                {                    
                    crew->first = new_work;                   
                    crew->last = new_work;               
                } 
                else
                {                    
                    crew->last->next = new_work;                    
                    crew->last = new_work;               
                }

                crew->work_count++;
                pthread_cond_signal(&crew->go);
                pthread_mutex_unlock(&crew->mutex);
                
            }
            
            closedir (dir);
        }
        /*普通文件*/
        else if (S_ISREG(filestat.st_mode))
        {
            FILE *search;            
            char buffer[256], *bufptr, *search_ptr;            
            search = fopen (work->path, "r");            
            if (search == NULL)  
            {
                fprintf(stderr, "Unable to open %s: %d (%s)\n", work->path, errno, strerror (errno));
            }
            else 
            {                
                while (1)
                {                   
                    bufptr = fgets(buffer, sizeof (buffer), search);                  
                    if (bufptr == NULL)
                    {                       
                        if (feof (search))                            
                            break;                      
                        if (ferror (search))
                        {                            
                            fprintf(stderr, "Unable to read %s: %d (%s)\n", work->path, errno, strerror(errno));
                            break;                       
                        }                    
                    }                    
                    
                    search_ptr = strstr(buffer, work->string);                    
                    if (search_ptr != NULL)
                    {               
                        /*文件锁*/
                        flockfile (stdout);                  
                        printf("Thread %d found \"%s\" in %s\n", mine->index, work->string, work->path);
#if 0                       
                        printf ("%s\n", buffer);
#endif                  
                        /**/
                        funlockfile (stdout);                      
                        break;                   
                    }               
                }              
                fclose (search);       
            }
        }
        else
        {
            fprintf(stderr, "Thread %d: %s is type %o (%s))\n", mine->index, work->path, filestat.st_mode & S_IFMT,
                         (S_ISFIFO (filestat.st_mode) ? "FIFO"  : 
                         (S_ISCHR (filestat.st_mode) ? "CHR" : 
                         (S_ISBLK (filestat.st_mode) ? "BLK"  :
                         (S_ISSOCK (filestat.st_mode) ? "SOCK" : "unknown")))));
        }


        free(work->path);
        free (work);

        pthread_mutex_lock (&crew->mutex);

        crew->work_count--;

        if (crew->work_count <= 0)
        {
            pthread_cond_broadcast (&crew->done);
            pthread_mutex_unlock (&crew->mutex);
            break;
        }

        pthread_mutex_unlock (&crew->mutex);
        
    }
    
    free (entry);
    return NULL;
}


int crew_start(crew_p crew, char *filepath, char *search)
{
    work_p request;
    int status;

    pthread_mutex_lock(&crew->mutex);

    while (crew->work_count > 0)
    {
        pthread_cond_wait(&crew->done, &crew->mutex);
    }

    errno = 0;
    /*获取相对路径名的最大字节数,包括终止符NULL*/
    path_max = pathconf(filepath, _PC_PATH_MAX);
    if (path_max == -1)
    {
        if (errno == 0)
            path_max = 1024;/*返错且不设置errno,则说明无限制*/
        else
            errno_abort("Unable to get PATH_MAX");
    }

    errno = 0;
    /*获取文件名的最大字节数,不包括终止符NULL*/
    name_max = pathconf(filepath, _PC_NAME_MAX);/**/
    if (name_max == -1)
    {
        if (errno == 0)
            name_max = 256;
        else
            errno_abort("Unable to get NAME_MAX");
    }
    
    name_max++;/*加上终止符的字节*/

    request = (work_p)malloc(sizeof(work_t));
    if (request == NULL)
        errno_abort("unable to allocate request");

    request->path = (char *)malloc(path_max);
    if (request->path == NULL)
        errno_abort("unable to allocate path");
    strcpy(request->path, filepath);
    request->string = search;
    request->next = NULL;

    /*将request插入到first的尾端*/
    if (crew->first == NULL)
    {
        crew->first = request;
        crew->last = request;
    }
    else
    {
        crew->last->next = request;
        crew->last = request;
    }

    crew->work_count++;

    status = pthread_cond_signal(&crew->go);
    if (status != 0)
    {
        free(crew->first);
        crew->first = NULL;
        crew->work_count = 0;
        pthread_mutex_unlock(&crew->mutex);

        return status;
    }

    while (crew->work_count > 0)
    {
        pthread_cond_wait(&crew->done, &crew->mutex);
    }

    pthread_mutex_unlock(&crew->mutex);

    return 0;
}


/*创建工作组*/
int crew_create(crew_t *crew, int crew_size)
{
    int crew_index;
    int status;

    if (crew_size > CREW_SIZE)
        return EINVAL;

    crew->crew_size = crew_size;
    crew->work_count = 0;
    crew->first = NULL;
    crew->last = NULL;

    status = pthread_mutex_init(&crew->mutex, NULL);
    if (status != 0)
        return status;
    status = pthread_cond_init(&crew->done, NULL);
    if (status != 0)
        return status;
    status = pthread_cond_init(&crew->go, NULL);
    if (status != 0)
        return status;

    /*创建四个线程*/
    for (crew_index = 0; crew_index < CREW_SIZE; crew_index++)
    {
        crew->crew[crew_index].index = crew_index;
        crew->crew[crew_index].crew = crew;/*回指*/
        status = pthread_create(&crew->crew[crew_index].thread, NULL, worker_routine, (void *)&crew->crew[crew_index]);
        if (status != 0)
            err_abort(status, "create worker");
    }

    return 0;
}

int main(int argc, char *argv[])
{
    crew_t my_crew;
    char line[128], *next;
    int status;

    if (argc < 3)
    {
        fprintf(stderr, "Usage: %s string path\n", argv[0]);
        return -1;
    }

    /*创建工作组*/
    status = crew_create(&my_crew, CREW_SIZE);
    if (status != 0)
        err_abort(status, "create crew");

    /*工作组开始运行*/
    status = crew_start(&my_crew, argv[2], argv[1]);
    if (status != 0)
        err_abort(status, "start crew");
    
    return 0;
}

4客户端/服务器


#include <pthread.h>
#include <math.h>
#include "errors.h"

#define CLIENT_THREADS  4               /* Number of clients */

#define REQ_READ        1               /* Read with prompt */
#define REQ_WRITE       2               /* Write */
#define REQ_QUIT        3               /* Quit server */


typedef struct request_tag {
    struct request_tag  *next;          /* Link to next */
    int                 operation;      /* Function code */
    int                 synchronous;    /* Non-zero if synchronous */
    int                 done_flag;      /* Predicate for wait */
    pthread_cond_t      done;           /* Wait for completion */
    char                prompt[32];     /* Prompt string for reads */
    char                text[128];      /* Read/write text */
} request_t;

typedef struct tty_server_tag {
    request_t           *first;
    request_t           *last;
    int                 running;
    pthread_mutex_t     mutex;
    pthread_cond_t      request;
} tty_server_t;

tty_server_t tty_server = {
    NULL, NULL, 0,
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};


int client_threads;
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clients_done = PTHREAD_COND_INITIALIZER;


void *tty_server_routine (void *arg)
{
    static pthread_mutex_t prompt_mutex = PTHREAD_MUTEX_INITIALIZER;
    request_t *request;
    int operation, len;
    int status;

    while (1) {
        status = pthread_mutex_lock (&tty_server.mutex);
        if (status != 0)
            err_abort (status, "Lock server mutex");

        while (tty_server.first == NULL) {
            status = pthread_cond_wait (
                &tty_server.request, &tty_server.mutex);
            if (status != 0)
                err_abort (status, "Wait for request");
        }
        request = tty_server.first;
        tty_server.first = request->next;
        if (tty_server.first == NULL)
            tty_server.last = NULL;
        status = pthread_mutex_unlock (&tty_server.mutex);
        if (status != 0)
            err_abort (status, "Unlock server mutex");

        operation = request->operation;
        switch (operation) {
            case REQ_QUIT:
                break;
            case REQ_READ:
                if (strlen (request->prompt) > 0)
                    printf (request->prompt);
                if (fgets (request->text, 128, stdin) == NULL)
                {
                    request->text[0] = '\0';
                }

                len = strlen (request->text);
                if (len > 0 && request->text[len-1] == '\n')
                {
                    request->text[len-1] = '\0';
                }
                break;
            case REQ_WRITE:
                puts (request->text);
                break;
            default:
                break;
        }
        if (request->synchronous) {
            status = pthread_mutex_lock (&tty_server.mutex);
            if (status != 0)
                err_abort (status, "Lock server mutex");
            request->done_flag = 1;
            status = pthread_cond_signal (&request->done);
            if (status != 0)
                err_abort (status, "Signal server condition");
            status = pthread_mutex_unlock (&tty_server.mutex);
            if (status != 0)
                err_abort (status, "Unlock server mutex");
        } else
            free (request);
        if (operation == REQ_QUIT)
            break;
    }
    return NULL;
}


void tty_server_request (
    int         operation,
    int         sync,
    const char  *prompt,
    char        *string)
{
    request_t *request;
    int status;

    status = pthread_mutex_lock (&tty_server.mutex);
    if (status != 0)
        err_abort (status, "Lock server mutex");
    if (!tty_server.running) {
        pthread_t thread;
        pthread_attr_t detached_attr;

        status = pthread_attr_init (&detached_attr);
        if (status != 0)
            err_abort (status, "Init attributes object");
        status = pthread_attr_setdetachstate (
            &detached_attr, PTHREAD_CREATE_DETACHED);
        if (status != 0)
            err_abort (status, "Set detach state");
        tty_server.running = 1;
        status = pthread_create (&thread, &detached_attr,
            tty_server_routine, NULL);
        if (status != 0)
            err_abort (status, "Create server");

        pthread_attr_destroy (&detached_attr);
    }

    request = (request_t*)malloc (sizeof (request_t));
    if (request == NULL)
        errno_abort ("Allocate request");
    request->next = NULL;
    request->operation = operation;
    request->synchronous = sync;
    if (sync) {
        request->done_flag = 0;
        status = pthread_cond_init (&request->done, NULL);
        if (status != 0)
            err_abort (status, "Init request condition");
    }
    if (prompt != NULL)
        strncpy (request->prompt, prompt, 32);
    else
        request->prompt[0] = '\0';
    if (operation == REQ_WRITE && string != NULL)
        strncpy (request->text, string, 128);
    else
        request->text[0] = '\0';


    if (tty_server.first == NULL) 
    {
        tty_server.first = request;
        tty_server.last = request;
    } 
    else 
    {
        (tty_server.last)->next = request;
        tty_server.last = request;
    }


    status = pthread_cond_signal (&tty_server.request);
    if (status != 0)
        err_abort (status, "Wake server");


    if (sync) {
        while (!request->done_flag) {
            status = pthread_cond_wait (
                &request->done, &tty_server.mutex);
            if (status != 0)
                err_abort (status, "Wait for sync request");
        }
        if (operation == REQ_READ) {
            if (strlen (request->text) > 0)
                strcpy (string, request->text);
            else
                string[0] = '\0';
        }
        status = pthread_cond_destroy (&request->done);
        if (status != 0)
            err_abort (status, "Destroy request condition");
        free (request);
    }
    status = pthread_mutex_unlock (&tty_server.mutex);
    if (status != 0)
        err_abort (status, "Unlock mutex");
}


void *client_routine (void *arg)
{
    int my_number = (int)arg, loops;
    char prompt[32];
    char string[128], formatted[128];
    int status;

    sprintf (prompt, "Client %d> ", my_number);
    while (1) {
        /*向服务器线程发送读请求*/
        tty_server_request (REQ_READ, 1, prompt, string);
        if (strlen(string) == 0)
        {
            printf("enter nothing ...\n");
            break;
        }
        for (loops = 0; loops < 4; loops++) {
            sprintf(formatted, "(%d#%d) %s", my_number, loops, string);
            /*向服务器线程发送写请求*/
            tty_server_request (REQ_WRITE, 0, NULL, formatted);
            sleep(1);
        }
    }
    status = pthread_mutex_lock (&client_mutex);
    if (status != 0)
        err_abort (status, "Lock client mutex");
    client_threads--;
    if (client_threads <= 0) {
        status = pthread_cond_signal (&clients_done);
        if (status != 0)
            err_abort (status, "Signal clients done");
    }
    status = pthread_mutex_unlock (&client_mutex);
    if (status != 0)
        err_abort (status, "Unlock client mutex");
    return NULL;
}

int main (int argc, char *argv[])
{
    pthread_t thread;
    int count;
    int status;

    /*创建4个客户线程*/
    client_threads = CLIENT_THREADS;
    for (count = 0; count < client_threads; count++) {
        status = pthread_create (&thread, NULL,
            client_routine, (void*)count);
        if (status != 0)
            err_abort (status, "Create client thread");
    }
    status = pthread_mutex_lock (&client_mutex);
    if (status != 0)
        err_abort (status, "Lock client mutex");
    while (client_threads > 0) {
        status = pthread_cond_wait (&clients_done, &client_mutex);
        if (status != 0)
            err_abort (status, "Wait for clients to finish");
    }
    status = pthread_mutex_unlock (&client_mutex);
    if (status != 0)
        err_abort (status, "Unlock client mutex");
    printf ("All clients done\n");
    tty_server_request (REQ_QUIT, 1, NULL, NULL);
    return 0;
}




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

POSIX多线程程序设计(第4章:使用线程的几种方式) 的相关文章

  • 操作系统3-覆盖技术,交换技术,虚拟存储,置换算法——lab3

    虚拟存储 在非连续存储内存分配的基础上 可以把一部分内容放到外存的做法 需求背景 增长迅速的存储需求 程序规模的增长速度远远大于存储器容量的增长速度 理想的存储器 更大 更快 更便宜 非易失性 实际张的存储器是目前很难达到的 要把硬盘的空间
  • Java 获取过去12个月日期

    业务功能要求 查询前12个月的相关数据统计 前 12个月日期 LocalDate today LocalDate now for long i 0L i lt 11L i LocalDate localDate today minusMon
  • PTA乙级1011 A+B 和 C

    给定区间 2 31 2 31 内的 3 个整数 A B 和 C 请判断 A B 是否大于 C 输入格式 输入第 1 行给出正整数 T 10 是测试用例的个数 随后给出 T 组测试用例 每组占一行 顺序给出 A B 和 C 整数间以空格分隔
  • 牛客网:坐标计算工具

    题目描述 开发一个坐标计算工具 A表示向左移动 D表示向右移动 W表示向上移动 S表示向下移动 从 0 0 点开始移动 从输入字符串里面读取一些坐标 并将最终输入结果输出到输出文件里面 输入 合法坐标为A 或者D或者W或者S 数字 两位以内
  • Maven 应用总结(持续更新)

    继承 模块化 模块化 在开发一个项目时 通常会进行模块化拆包 如下 blog parent blog controller blog service blog entity blog util 根据模块的名称可以看出 业务是比较单一的 若是
  • HTML 知识扫盲

    写在前面 HTML 是一门超文本标记语言 不管你听没听说过 HTML 但在网上冲浪的途中你无时不刻都在与它接触 他遍布在每个你看得到的互联网的角落 其实对于笔者而言 我已经断断续续地学习过这门语言 经过时间的磋磨 所剩知识也是寥寥无几 这次
  • autotools automake autoconf libtool 笔记

    autotools automake autoconf libtool 笔记 默认安装路径为 prefix usr local 可以通过 configure prefix
  • c语言 变量 section,#pragma DATA_SECTION的解释

    DSP CMD文件的编写EETOP专业博客 电子工程师自己的家园L gZnWr 1 COFF格式 1 gt 通用目标文件格式 Common Object File Format 是一种流行的二进制可执行文件格式 二进制可执行文件包括库文件
  • Python真的能杀死Excel吗?它能实现哪些Excel功能?

    在大家的印象里 想进入金融行业或者数据岗位 首先需要精通Excel 而且现在招聘条件也是明确表示 要精通Excel等办公软件 后面还会加一句 有Python经验的优先 野村证券副首席数字官马修 汉普森在上周五的伦敦Quant Confere
  • dnspod每步nat123及3322动态域名同类端口映射域名解析软件对比

    首先说明下这三个名词 端口映射 域名解析 动态域名解析 端口映射 80端口映射或自定义端口映射 可以解决80端口被屏蔽 可以在内网使用直接发布网站应用 可以在内网使用实现外网访问内网应用 域名解析 也叫域名解析管理 常用的操作就是登录域名管
  • 【论文复现】—— A New Outlier Removal Strategy Based on Reliability of Correspondence Graph for Fast ....

    目录 一 算法原理 1 论文概述 2 实现流程 3 参考文献 二 代码实现 三 GitHub链接 一 算法原理 1 论文概述 三维激光扫描技术能够为实景测量与重建提供高精度 大范围的点云数据 然而单一来源的点云不够完整 不够精细 因此使用点
  • Spring IOC(四)doCreateBean方法源码分析——创建Bean的过程

    目录 第1步 createBeanInstance 得到实例BeanWrapper 第1 1步 通过工厂方法实例化 先略过 第1 2步 通过默认构造函数实例化 第1 3步 通过有参构造函数实例化 第2步 applyMergedBeanDef
  • 类的私域变量访问方法

    类的私域变量只能在类的内部使用 类的实例对象不能访问 class JustCounter 类声明 secrectCount 0 私有变量 publicCount 0 公开变量 def count self 类方法 self secrectC
  • 关于String的hashcode,以及判断字符串是否相等的解析

    跟着老师的方法验证equals方法的重写 由于误写发现运行结果和预想的不一样 先上代码 package com wuyw oo import java util Objects author wuyw2020 date 2019 10 28
  • 关于Linux下操作(写)excel文件

    网上有很多第三方库可以非常方便的读写excel文件在此不再列举 如果应用场景只是将数据记录成excel格式 只写不读 一种不利用第三方库的做法是 1 正常创建一个文件 后缀为excel格式之一即可 2 sprintf格式化字符串生成列头 比
  • 40张最全计算机网络基础思维导图

    hi 大家好 今天的网络基础思维导图主要是二 三层网络协议 再配合之前的文章 四层以上互联网核心协议 TCP IP协议精华指南pdf发布 非常适合对网络感兴趣的同学 同时也让大家对整个网络架构有一个整体的认识 希望大家喜欢 传输介质简介 以
  • 几种网络通信协议

    应用层 HTTP Hypertext Transfer Protocol 超文本传输协议 显示网页 DNS Domain Name System 域名系统 FTP File Transfer Protocol 文件传出协议 SFTP SSH
  • BAPI_GOODSMVT_CREATE物料凭证创建…

    BAPI GOODSMVT CREATE可以实现物料凭证创建和部分冲销 全部冲销可以使用BAPI GOODSMVT CANCEL CALL FUNCTION BAPI GOODSMVT CREATE EXPORTING GOODSMVT H

随机推荐

  • Caffe中Solver解析

    1 Solver的初始化 shared ptr
  • QT中默认不选中Shadow build设置方法

    在使用qt开发的时候 每次创建工程 都会默认选中shadow build影子构建 作用是把编译生成的文件与源文件放在不同的目录 这样源码目录就相对整洁 但是这也会带来个问题 感觉超级鸡肋 就是经常修改了代码之后点运行 跑的还是修改之前的程序
  • 使用VMware Workstation Player虚拟机安装Linux系统

    下载安装 VMware Workstation Player 首先下载并安装 VMware Workstation Player VMware Workstation是一款非常强大的虚拟机软件 有pro专业版和player免费版 我们个人使
  • Java线程协作的两种方式

    介绍 Java中线程协作的最常见的两种方式 利用Object wait Object notify 和使用Condition 方法一 Object中的wait notify notifyAll方法定义如下 public final nati
  • 基于域名的恶意网站检测

    基于域名的恶意网站检测 0x00 数据来源 0x01 基于网页内容的判别方法 0x02 基于域名数据的判别方法 0x03 参考文献 0x00 数据来源 根据老师给的 300w 域名列表爬到的相应 DNS 响应数据 0x01 基于网页内容的判
  • 【翻译】 2.6 中的 4K 堆栈 [发布于 2004 年 5 月 12 日,作者:corbet

    传统上 Linux 内核在大多数架构上都使用 8KB 内核堆栈 该堆栈必须满足系统调用可能产生的任何调用序列 以及可能同时调用的任何 硬或软 中断处理程序的需要 实际上 在稳定的内核中 堆栈溢出的情况几乎闻所未闻 内核开发人员早已学会避免使
  • 【Docker教程(一)】WSL2、Docker以及docker-compose安装及环境配置

    一 如何安装WSL2 二 如何安装和配置docker 三 如何安装docker compose 四 如何基于ip代理池获取代理ip 注意 先安装WSL2 再安装docker和docker compose 参考资料 软件下载及安装教程 a d
  • JavaScript循环语句和分支语句

    JavaScript中的分支语句 1 if分支语句 2 if else语句 3 if else if else语句 4 switch语句 注 if语句可以单独存在 else语句不能单独存在 else找离自己最近的同一级别的if搭配成对 在i
  • linux创建711文件,linux权限---【600,644,700,755,711,666,777】,644711

    linux权限 600 644 700 755 711 666 777 644711 chmod命令详解 使用权限 所有使用者 使用方式 chmod cfvR help version mode file 说明 Linux Unix 的档案
  • 用streamlit,几行代码就可以拥有漂亮图表!

    大家注意 因为微信最近又改了推送机制 经常有小伙伴说错过了之前被删的文章 比如前阵子冒着风险写的爬虫 再比如一些限时福利 错过了就是错过了 所以建议大家加个星标 就能第一时间收到推送 大家好 我是爱搞事情的了不起 之前我们爬过懂车帝的车型评
  • c语言报告 列主元lu分解,求助:求个R语言的选主元LU分解。

    推荐答案 上善若水666 来自团队 乐于助人2018 04 21 采纳率 57 等级 50 已帮助 96万人 可以 这是数值分析书上的定理 就是存在排列矩阵P 对换矩阵的乘积 使得PA LU 这个定理说明先对A进行对换矩阵的行得到PA 然后
  • 【面试题】1、总结面试题1

    1 Java语言有哪些特点 1 面向对象 Java是一种面向对象的语言 支持封装 继承和多态等面向对象的特性 Java特别强调类和对象的关系 要求所有代码都必须位于类中 和Java一样很流行的Python也是面向对象的语言 但它对面向对象的
  • PVE虚拟化平台之安装Ubuntu Desktop系统

    PVE虚拟化平台之安装Ubuntu Desktop系统 一 Ubuntu介绍 1 1 Ubuntu简介 1 2 Ubuntu版本 1 3 ubuntu命名规则 二 上传镜像到PVE 2 1 检查PVE环境 2 2 上传镜像到PVE 三 新建
  • ARM软件测试库STL( Software Test libraries )介绍

    原文链接 https www arm com zh TW products development tools embedded and software software test libraries Faster Safety Comp
  • XXL-JOB 分布式定时任务调度平台

    文章目录 1 概述 2 使用 1 概述 首先我们要知道什么是XXL JOB 官方简介 XXL JOB是一个分布式任务调度平台 其核心设计目标是开发迅速 学习简单 轻量级 易扩展 现已开放源代码并接入多家公司线上产品线 开箱即用 XXL JO
  • JavaScript/ES6中的Object

    记录Object构造函数的一些静态方法 Object是编程中常见的一种构造函数 创建一个对象包装器 为什么说是创建了一个对象包装器 Object构造器会根据传入的参数的类型 将参数包装成具体的对象 如有数字对象 boolean对象 字符串对
  • 程序设计模式23+1种定义+UML图(有部分分析和联用)

    程序设计模式这门课已经学完了 复习的时候做了一个这样的汇总 希望可以给后来学习这门课的同学一些帮助 设计模式的分类 根据目的 模式是用来做什么的 可分为创建型 Creational 结构型 Structural 和行为型 Behaviora
  • 问题1:将静态库编译成动态库

    一 描述 1 多个同事间合作开发一个软件项目 每个人负责一部分模块开发 商定好彼此的对外接口 编程实现后 最后需要将所有人的开发代码编译链接到一起 生成一个最终目标文件 2 由于多人开发 若全部采用源文件编译 编译时文件数量众多 编写Mak
  • AES加解密,Base64编解码

    AES是什么 美国国家标准技术研究所在2001年发布了高级加密标准 AES AES是基于数据块的加密方式 即 每次处理的数据是一块 16字节 当数据不是16字节的倍数时填充 这就是所谓的分组密码 区别于基于比特位的流密码 16字节是分组长度
  • POSIX多线程程序设计(第4章:使用线程的几种方式)

    1概述 线程编程模型有以下三种方式 模型 说明 流水线 每个线程反复地在数据系列集上执行同一种操作 并把操作结果传递给下一步骤的其他线程 这就是 流水线 方式 即流水线上的线程对数据执行同一种操作 如简单的加1 再把数据的操作结果传递给下一