首页 > 其他分享 >操作系统实验-线程同步

操作系统实验-线程同步

时间:2023-05-08 15:23:03浏览次数:50  
标签:count 同步 操作系统 int 写者 beg 线程 ord NULL

OS实验一:线程同步

使用Windows提供的API线程接口实现。

参考:C++创建线程示例C++多线程微软多线程编程文档

线程创建与撤销

参数说明

LPVOID 是无类型指针,做形参可接收任意类型的指针

Void ExitThread(DWORD dwExitCode) 在线程函数内执行该线程的撤销,等价于内部的return。

Bool TerminateThread( HANDLE hThread, DWORD dwExitCode) 在线程函数外部强行终止指定线程,并赋予指定结束码。

Sleep(DWORD dwMilliseconds) 挂起当前线程,dwMilliseconds为挂起传入时间,单位为ms;取值为INFINITE则无限延迟,设为0ms则立即交出时间片。

Bool CloseHandle(HANDLE hObject) 关闭句柄

CreateThread() 参数1为安全策略,参数2为初始栈规模,参数3为回调函数(有指定格式),参数4为回调函数的参数(一个结构体),参数5为线程何时开始运行,参数6为获取线程id的指针。
示例:CreateThread(NULL, 0, test, NULL, 0, NULL) 表示安全策略默认;初始栈规模默认;函数使用形如:

DWORD WINAPI test(LPVOID);

的test函数;无传入参数;线程创建后立即执行;不读取线程id。

源码

#include <thread>
#include <stdio.h>
#include <iostream>
#include <windows.h>
using namespace std;

DWORD WINAPI hello(LPVOID){
    cout<<"hello world!\n";
    return 0;
}

DWORD WINAPI name(LPVOID){
    cout<<"My name is ZYX.\n";
    return 0;
}

DWORD WINAPI timely(LPVOID){
    cout<<"Please wait for 5s.\n";
    Sleep(5000);
    cout<<"I wake up!"<<endl;
    ExitThread(0);  // 等价于 return 0;
}
int main(){
    HANDLE threada, threadb, threadc;
    threada = CreateThread(NULL, 0, hello, NULL, 0, NULL);
    threadb = CreateThread(NULL, 0, name, NULL, 0, NULL);
    threadc = CreateThread(NULL, 0, timely, NULL, 0, NULL);
    Sleep(6000);
    CloseHandle(threada);
    CloseHandle(threadb);
    CloseHandle(threadc);
    cout<<"Main thread ends.";
    return 1;
}

线程同步

参考:C++信号量用法

参数说明

CreateSemaphore() 创建信号量并返回一个句柄,该句柄用于表示特定的信号量对象。
参数1表示安全策略,参数2表示基础资源数量,参数3表示最大资源数量,参数4为信号量命名。
同一命名创建的信号量是共享实时资源数量的

OpenSemaphore() 表示打开一类已有的信号量,赋予一个新的句柄一个新的信号量对象。

WaitForSingleObject(Semaphore, INFINITE) 会等待信号量处于信号态(资源数量>0)再继续执行,同时会使目标信号量减一,即再次占用资源。

源码

#include <iostream>
#include <stdio.h>
#include <Windows.h>
using namespace std;

DWORD WINAPI test(LPVOID);
HANDLE myThread, mySemaphore;

int main(){
    CreateSemaphore(NULL, 0, 1, "A");
    mySemaphore = OpenSemaphore(SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, NULL, "A");
    myThread = CreateThread(NULL, 0, test, NULL, 0, NULL);
    WaitForSingleObject(mySemaphore, INFINITE);
    CloseHandle(mySemaphore);
    CloseHandle(myThread);
    cout<<"Main thread ends.";
    return 1;
}

DWORD WINAPI test(LPVOID par){
    Sleep(5000);
    cout<<"I am over.\n";
    ReleaseSemaphore(mySemaphore, 1, NULL);
    return 0;
}

线程互斥

参考:C++互斥量-信号量-临界区Windows临界区使用C++临界区-互斥对象-事件对象区别

源码

#include <iostream>
#include <Windows.h>
#include <stdio.h>
using namespace std;

CRITICAL_SECTION cs;    // 临界区

DWORD WINAPI cFunc1(LPVOID);
DWORD WINAPI cFunc2(LPVOID);

int cCnt = 0;

bool CriticalTest(){
    // 初始化临界区
    InitializeCriticalSection(&cs);
    HANDLE h1, h2;
    h1 = CreateThread(NULL, 0, cFunc1, NULL, 0, NULL);
    h2 = CreateThread(NULL, 0, cFunc2, NULL, 0, NULL);
    WaitForSingleObject(h1, INFINITE);
    WaitForSingleObject(h2, INFINITE);
    DeleteCriticalSection(&cs);
    CloseHandle(h1);
    CloseHandle(h2);
    return 1;
}

DWORD WINAPI cFunc1(LPVOID){
    cout<<"func1调用并等待进入临界区\n";
    EnterCriticalSection(&cs);
    cout<<"func1进入临界区\n";
    cCnt += 1;
    printf("count = %d\n", cCnt);
    cout<<"func1退出临界区\n";
    LeaveCriticalSection(&cs);
    return 1;
}

DWORD WINAPI cFunc2(LPVOID){
    cout<<"func2调用并等待进入临界区\n";
    EnterCriticalSection(&cs);
    cout<<"func2进入临界区\n";
    cCnt += 1;
    printf("count = %d\n", cCnt);
    cout<<"func2退出临界区\n";
    LeaveCriticalSection(&cs);
    return 1;
}

HANDLE Mutex;

DWORD WINAPI mFunc1(LPVOID);
DWORD WINAPI mFunc2(LPVOID);

int mCnt = 0;

bool MutexTest(){
    // 初始化临界区
    Mutex = CreateMutex(NULL, 0, "A");

    HANDLE h1, h2;
    h1 = CreateThread(NULL, 0, mFunc1, NULL, 0, NULL);
    h2 = CreateThread(NULL, 0, mFunc2, NULL, 0, NULL);
    
    WaitForSingleObject(h1, INFINITE);
    WaitForSingleObject(h2, INFINITE);
    
    CloseHandle(Mutex);
    CloseHandle(h1);
    CloseHandle(h2);
    return 1;
}

DWORD WINAPI mFunc1(LPVOID){
    cout<<"func1调用并等待互斥信号\n";
    WaitForSingleObject(Mutex, INFINITE);
    cout<<"func1通过互斥信号\n";
    mCnt += 1;
    printf("count = %d\n", mCnt);
    cout<<"func1释放互斥信号\n";
    ReleaseMutex(Mutex);
    return 1;

}

DWORD WINAPI mFunc2(LPVOID){
    cout<<"func2调用并等待互斥信号\n";
    WaitForSingleObject(Mutex, INFINITE);
    cout<<"func2通过互斥信号\n";
    mCnt += 1;
    printf("count = %d\n", mCnt);
    cout<<"func2释放互斥信号\n";
    ReleaseMutex(Mutex);
    return 1;

}

int main(){
    cout<<"---------临界区实现线程同步---------"<<endl;
    CriticalTest();
    cout<<"----------互斥实现线程同步----------"<<endl;
    MutexTest();
    return 1;
}

管道通信

参考:C++的创建命名管道C++进程通信之命名管道C++命名管道详解

参数说明

PeekNamedPipe 函数将数据从命名管道或匿名管道复制到缓冲区中,而不将其从管道中删除。 它还返回有关管道中的数据的信息。

BOOL PeekNamedPipe(
  [in]            HANDLE  hNamedPipe,
  [out, optional] LPVOID  lpBuffer,
  [in]            DWORD   nBufferSize,
  [out, optional] LPDWORD lpBytesRead,
  [out, optional] LPDWORD lpTotalBytesAvail,
  [out, optional] LPDWORD lpBytesLeftThisMessage
);

该函数有特性:

函数始终在单线程应用程序中立即返回,即使管道中没有数据也是如此。 命名管道句柄 (阻塞或非阻塞) 的等待模式对函数没有影响。

这使得它不受等待模式限制,不会被阻塞。

对于 ReadFile() ,其是否等待受命名管道句柄的等待模式影响,但 PIPE_NOWAIT 同时对 ReadFile, WriteFile, ConnectNamedPipe 生效,这会导致连接管道时就不等待,管道的建立提升难度。

源码

服务端:

#include <Windows.h>
#include <iostream>
using namespace std;
#define MAX_BUF 255

HANDLE pipe;
HANDLE rec;

int readFile(HANDLE pipe, char * str, string& fstr){
    bool suc = false;
    DWORD len = 0;
    int cnt = 0;
    PeekNamedPipe(pipe, str, MAX_BUF * sizeof(char), &len, NULL, NULL);
    if(len <= 0){   // 无消息可读
        return 0;
    }
    while(1){
        suc = ReadFile(pipe, str, MAX_BUF * sizeof(char), &len, NULL);    // 每次读取到最多255个字符
        fstr.append(str);   // 接入到字符串
        if(len>0) cnt++;
        if(!suc || len<MAX_BUF){    // 读取失败或者读取完毕(个数小于最大数)
            return cnt;
        }
    }
}

DWORD WINAPI recvCheck(LPVOID){
    while(1){
        Sleep(1000);    // 每隔一秒检测是否有接收到信息
        char buf[MAX_BUF] = "";
        string finalStr = "";
        int flag;
        flag = readFile(pipe, buf, finalStr);
        switch(flag){
            case 0:{
                break;
            }
            default:{
                cout<<"From Client: "<<finalStr<<endl;
            }
        }
    }
    return 1;
}

int main(){
    cout<<"服务端上线\n"<<"创建命名管道并等待连接...\n";
    pipe = CreateNamedPipe(TEXT("\\\\.\\Pipe\\zyx"),  // 管道名称
        PIPE_ACCESS_DUPLEX,     // 双向管道
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,  // 信息流且等待
        PIPE_UNLIMITED_INSTANCES,   // 示例数量无上限(255)
        0,      // 输出缓冲区保留0
        0,      // 输入缓冲区保留0
        1000,   // 等待时长无限
        0); // 安全策略默认
    if(pipe == INVALID_HANDLE_VALUE || pipe == NULL){
        cout<<"管道创建失败!\n"<<GetLastError();
        return 1;
    }
    // 等待连接
    if(ConnectNamedPipe(pipe, NULL) != NULL){
        cout<<"连接成功,开始通信!\n";
        rec = CreateThread(NULL, 0, recvCheck, NULL, 0, NULL);
        string inC;
        DWORD dwWrite;
        bool flag;
        while(1){
            getline(cin, inC);  // cin忽略空白字符,因此它实际上接收不到为空的内容,换成getline就可以
            if(inC.length()==0){    // 输入为空
                // 啥也不做
            } else {
                if(inC=="end") break;   // 退出
                else{   // 写入数据并传送
                    flag = WriteFile(pipe, inC.c_str(), inC.length(), NULL, NULL);
                    if(!flag){
                        cout<<"Failed to sent data!\n";
                        return 1;
                    }
                    cout<<"To Client: "<<inC<<endl;
                }
            }
        }
    } else {
        cout<<"管道连接失败!";
    }

    cout<<"已经关闭管道\n";
    DisconnectNamedPipe(pipe);  // 关闭管道连接
    TerminateThread(rec, 0);   // 终止接收检测
    CloseHandle(rec);
    CloseHandle(pipe);
    cout<<"服务端下线\n";
    system("pause");
    return 1;
}

客户端:

// 客户端
#include <Windows.h>
#include <iostream>
#include <conio.h>
using namespace std;
#define MAX_BUF 255
    
HANDLE pipe, rec;

int readFile(HANDLE pipe, char * str, string& fstr){
    bool suc = false;
    DWORD len = 0;
    int cnt = 0;
    PeekNamedPipe(pipe, str, MAX_BUF * sizeof(char), &len, NULL, NULL);
    if(len <= 0){   // 无消息可读
        return 0;
    }
    while(1){
        suc = ReadFile(pipe, str, MAX_BUF * sizeof(char), &len, NULL);    // 每次读取到最多255个字符
        fstr.append(str);   // 接入到字符串
        if(len>0) cnt++;
        if(!suc || len<MAX_BUF){    // 读取失败或者读取完毕(个数小于最大数)
            return cnt;
        }
    }
}

DWORD WINAPI recvCheck(LPVOID){
    while(1){
        Sleep(1000);    // 每隔一秒检测是否有接收到信息
        char buf[MAX_BUF] = "";
        string finalStr = "";
        int flag;
        flag = readFile(pipe, buf, finalStr);
        switch(flag){
            case 0:{
                break;
            }
            default:{
                cout<<"From Server: "<<finalStr<<endl;
            }
        }
    }
    return 1;
}

int main(){
    cout<<"客户端上线\n"<<"按任意键开始连接命名管道...\n";
    _getch();
    cout<<"等待命名管道...\n";
    if(WaitNamedPipe(TEXT("\\\\.\\Pipe\\zyx"), NMPWAIT_WAIT_FOREVER) == FALSE){ // 若命名管道没有打开则直接退出
        cout<<"命名管道未上线";
        return 0;
    }
    cout<<"成功打开命名管道\n";

    // 开始连接命名管道
    pipe = CreateFile(TEXT("\\\\.\\Pipe\\zyx"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(pipe == NULL || pipe == INVALID_HANDLE_VALUE){
        cout<<"管道连接失败!\n"<<GetLastError();
        return 1;
    }
    cout<<"连接成功,开始通信!\n";
    rec = CreateThread(NULL, 0, recvCheck, NULL, 0, NULL);
    string inC;
    DWORD dwWrite;
    while(1){
        char buf[MAX_BUF] = "";
        string finalStr = "";
        int flag;
        getline(cin, inC);
        if(inC.length()==0){    // 尝试接收管道信息
            // 啥也不做
        } else {
            if(inC=="end") break;   // 退出
            else{   // 写入数据并传送
                flag = WriteFile(pipe, inC.c_str(), inC.length(), NULL, NULL);
                if(!flag){
                    cout<<"Failed to sent data!";
                    return 1;
                }
                cout<<"To Server: "<<inC<<endl;
            }
        }
    }

    cout<<"已经关闭管道\n";
    DisconnectNamedPipe(pipe);
    TerminateThread(rec, 0);   // 终止接收检测
    CloseHandle(rec);
    CloseHandle(pipe);
    cout<<"客户端下线\n";
    system("pause");
    return 0;
}

通过创建子线程,定时检测有无收到管道另一端发送的数据,来实现伪实时交互,但实际上退出还是需要两端都输入end,因为一端并不能实时检测管道是否关闭并同步结束另外几个线程(把发送数据写成子线程也许可以)。

实际上管道的关闭体现在发送数据出错。

读者-写者问题

参考:读者0写者问题

概念理解:

  1. 关于读者优先,实验说明书中有如下描述:

    读者优先的附加限制:若一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。
    
    读者优先指的是除非有写者在写文件,否则读者不需要等待。
    

    后半句附加了一个条件:有读者在等待队列时写者不做写操作,这与前半段的描述实际上是不完全一样的,但后半段概念上与"写者优先"相似,我称之为"绝对优先"思想;个人偏向于前半段的描述是对"读者优先"的准确理解,实际上读写者都在队列中时依然是按照队列顺序进行操作的。

  2. 有一个细节是读者可以多个读者同时读,因此它在"写者优先"下判定优先锁的时候是在启动时间立刻判定,若是有一个读者与写者同时启动,由于计算机内部"同时"可能意味着微小的相差,因此实际上"同时"启动的读者可能抢在写者的前面判定了优先锁,它就打破了"写者优先"的垄断,但这是不可控力导致的不绝对优先,代码逻辑上仍然是锁死了"写者优先"的。

    而写者必须等待上个写者写完才能继续执行写操作,因此在"读者绝对优先"思想中,他判定优先锁理论上是在上一个写者写完(或上一群读者全部读完),而不是在启动时;换言之,重要的是轮到它获取资源时有没有读者在等待,而不是它申请资源时有没有读者在等待,因此判定优先锁的位置和方式不同于读者。

    但是要实现这样的优先就必须要让得到资源的写者在检测到读者时让出资源,导致出现多余的线程切换,因此实现的代码中并没有采用"写者绝对优先思想"。

  3. 最终实现的"读者优先"代码参考的是:申请资源时有无读者在等候。

  4. 因此有同时启动的读者和写者出现的测试案例,实际上是没有意义的。

源码

写者优先:

#include <Windows.h>
#include <iostream>
#include <fstream>
using namespace std;

#define MAX_TRD 255
int trd_count = 0;

struct info{
    int ord;    // 线程号
    int beg;    // 开始时间
    int pause;  // 持续时间
};

int read_count = 0;
int write_count = 0;

HANDLE rec, red, wfr, wte;

DWORD WINAPI write(LPVOID param){   // 写者
    info *pam = (info*)param;
    int beg = pam->beg, pause = pam->pause, ord = pam->ord;
    printf("Writer thread %d is created.\n", ord, beg);
    Sleep(beg); // beg秒后启动
    WaitForSingleObject(wte, INFINITE);
    write_count += 1;
    if(write_count == 1){   // 第一位(等待/执行)的写者占用优先锁
        WaitForSingleObject(wfr, INFINITE);
    }
    ReleaseSemaphore(wte, 1, NULL);
    printf("Writer thread %d applys for the resource.\n", ord);
    WaitForSingleObject(rec, INFINITE); // 申请资源
    printf("Writer thread %d is writing.\n", ord);
    Sleep(pause);   // 操作持续时间
    printf("Writer thread %d ends.\n", ord);
    WaitForSingleObject(wte, INFINITE);
    write_count -= 1;
    if(write_count == 0){   // 最后一位执行的写者释放优先锁
        ReleaseSemaphore(wfr, 1, NULL);
    }
    ReleaseSemaphore(wte, 1, NULL);
    printf("Writer thread %d releases the resource.\n", ord);   
    ReleaseSemaphore(rec, 1, NULL); // 释放资源
    return 1;
}

DWORD WINAPI read(LPVOID param){   // 读者
    info *pam = (info*)param;
    int beg = pam->beg, pause = pam->pause, ord = pam->ord;
    printf("Reader thread %d is created.\n", ord);
    Sleep(beg); // beg秒后启动
    WaitForSingleObject(wfr, INFINITE);   // 有无加入申请资源队列的资格
    WaitForSingleObject(red, INFINITE); // 申请资源
    read_count += 1;    // 写者数量
    if(read_count == 1){    // 第一位读者申请资源
        printf("Reader thread %d applys for the resource.\n", ord);
        WaitForSingleObject(rec, INFINITE); // 申请资源
    }
    ReleaseSemaphore(red, 1, NULL); // 释放读者数量
    ReleaseSemaphore(wfr, 1, NULL); // 不抢占优先锁,只检测是否被占用,若不被占用则有申请资源的资格,因而能算作"队列中的读者",可以计数
    printf("Reader thread %d is reading.\n", ord);
    Sleep(pause);   // 操作持续时间
    printf("Reader thread %d ends.\n", ord);  
    WaitForSingleObject(red, INFINITE); // 申请资源
    read_count -= 1;  
    if(read_count == 0){    // 最后一位读者释放资源
        printf("Reader thread %d releases the resource.\n", ord);
        ReleaseSemaphore(rec, 1, NULL); // 释放资源
    }
    ReleaseSemaphore(red, 1, NULL); // 释放读者数量
    return 1;
}

int main(){
    rec = CreateSemaphore(NULL, 1, 1, TEXT("resource"));
    red = CreateSemaphore(NULL, 1, 1, TEXT("reader"));
    wfr = CreateSemaphore(NULL, 1, 1, TEXT("writerFirst"));
    wte = CreateSemaphore(NULL, 1, 1, TEXT("writer"));
    info *tmp;
    HANDLE trd[MAX_TRD]; 
    
    fstream input;
    input.open("./input.txt", ios::in);
    string str;
    while(!input.eof()){
        string met;
        int ord, beg, pause;
        input>>ord;
        if(input.eof()){
            break;
        }
        input>>met;
        input>>beg;
        input>>pause;
        tmp = new info();
        tmp->ord = ord;
        tmp->beg = beg*1000;
        tmp->pause = pause*1000;
        if(met == "W"){
            trd[trd_count] = CreateThread(NULL, 0, write, tmp, 0, NULL);
            trd_count += 1;
        }
        if(met == "R"){
            trd[trd_count] = CreateThread(NULL, 0, read, tmp, 0, NULL);
            trd_count += 1;
        }
    }

    for(int i = 0; i < trd_count; i++){
        WaitForSingleObject(trd[i], INFINITE);
        CloseHandle(trd[i]);
    }

    CloseHandle(rec);
    CloseHandle(red);
    return 1;

}

读者优先:

#include <Windows.h>
#include <iostream>
#include <fstream>
using namespace std;

#define MAX_TRD 255
int trd_count = 0;

struct info{
    int ord;    // 线程号
    int beg;    // 开始时间
    int pause;  // 持续时间
};

int read_count = 0;

HANDLE rec, red;

DWORD WINAPI write(LPVOID param){   // 写者
    info *pam = (info*)param;
    int beg = pam->beg, pause = pam->pause, ord = pam->ord;
    printf("Writer %d is created.\n", ord, beg);
    Sleep(beg); // beg秒后启动
    printf("Writer %d applys for the resource.\n", ord);
    WaitForSingleObject(rec, INFINITE); // 申请资源
    printf("Writer %d is writing.\n", ord);
    Sleep(pause);   // 操作持续时间
    printf("Writer %d ends and releases the resource.\n", ord);
    ReleaseSemaphore(rec, 1, NULL); // 释放资源
}

DWORD WINAPI read(LPVOID param){   // 读者
    info *pam = (info*)param;
    int beg = pam->beg, pause = pam->pause, ord = pam->ord;
    printf("Reader %d is created.\n", ord);
    Sleep(beg); // beg秒后启动
    WaitForSingleObject(red, INFINITE); // 申请资源
    read_count += 1;    // 写者数量
    if(read_count == 1){    // 第一位读者申请资源
        printf("Reader %d applys for the resource.\n", ord);
        WaitForSingleObject(rec, INFINITE); // 申请资源
    }
    ReleaseSemaphore(red, 1, NULL); // 释放读者数量
    printf("Reader %d is reading.\n", ord);
    Sleep(pause);   // 操作持续时间
    printf("Reader %d ends.\n", ord);  
    WaitForSingleObject(red, INFINITE); // 申请资源
    read_count -= 1;  
    if(read_count == 0){    // 最后一位读者释放资源
        printf("Reader %d releases the resource.\n", ord);
        ReleaseSemaphore(rec, 1, NULL); // 释放资源
    }
    ReleaseSemaphore(red, 1, NULL); // 释放读者数量
}

int main(){
    rec = CreateSemaphore(NULL, 1, 1, TEXT("resource"));
    red = CreateSemaphore(NULL, 1, 1, TEXT("reader"));
    info *tmp;
    HANDLE trd[MAX_TRD]; 
    
    fstream input;
    input.open("./input.txt", ios::in);
    string str;
    while(!input.eof()){
        string met;
        int ord, beg, pause;
        input>>ord;
        if(input.eof()){
            break;
        }
        input>>met;
        input>>beg;
        input>>pause;
        tmp = new info();
        tmp->ord = ord;
        tmp->beg = beg*1000;
        tmp->pause = pause*1000;
        if(met == "W"){
            trd[trd_count] = CreateThread(NULL, 0, write, tmp, 0, NULL);
            trd_count += 1;
        }
        if(met == "R"){
            trd[trd_count] = CreateThread(NULL, 0, read, tmp, 0, NULL);
            trd_count += 1;
        }
    }

    for(int i = 0; i < trd_count; i++){
        WaitForSingleObject(trd[i], INFINITE);
        CloseHandle(trd[i]);
    }

    CloseHandle(rec);
    CloseHandle(red);
    return 1;

}

标签:count,同步,操作系统,int,写者,beg,线程,ord,NULL
From: https://www.cnblogs.com/Forest-set-you/p/17381862.html

相关文章

  • 将windows操作系统(win10)装入移动硬盘
    1.准备windows系统镜像比如我的iso镜像:zh-cn_windows_10_business_editions_version_22h2_updated_april_2023_x64_dvd_c03ed5aa.iso镜像挂载后可以看到关键文件 2.对移动硬盘进行分区 listdiskselectdiskncleanconvertgptselectpartition1deletepartiti......
  • 深度操作系统 deepin 20.8 定制的 SSH 安全模块问题
    近期由于工作需求,尝试使用国产化操作系统,由于对Debian系统的偏爱,选择了深度操作系统,使用的版本是Deepin20.8,安装使用基本顺利,没什么大问题。但由于这个环境是面向公网的,总是免不了大量的攻击类SSH登录尝试,发现深度好像定制修改了安全防护这块,当SSH密码登录错误达到指定次......
  • [Redis] 解决多个 Redis 服务同步删除有关联的 key
    以下内容基于ChatGPT的回答。我有多个redis服务,比如有A,B,C三个,他们供不同的业务服务使用。有一个需求是,A服务中删除一个key后,希望能同时删除B,C服务中有关联的key,但这个key名称不一样相同,关联关系是可配置的。请问有什么办法实现?解决方案可以使用Redis的发布......
  • redis到底是不是单线程
     常说的Redis是单线程,主要是指Redis对外提供键值存储服务的主要流程,即从「网络模块+命令处理」是由⼀个线程来完成的。除此外Redis的其他功能,比如持久化、异步删除、集群数据同步等,是由额外的线程执⾏的。并且,从redis6.0开始,网络模块开始支持多线程,命令处理仍是单线程......
  • Java守护线程daemon介绍
    1.介绍线程分为用户线程和守护线程JVM必须确保用户线程执行完毕,但是不用等待守护线程执行完毕示例代码:publicclassTestDaemon{publicstaticvoidmain(String[]args){DaemonThreaddaemon=newDaemonThread();CustomerThreadcustomer=ne......
  • Java多线程--让主线程等待所有子线…
    朋友让我帮忙写个程序从文本文档中导入数据到oracle数据库中,技术上没有什么难度,文档的格式都是固定的只要对应数据库中的字段解析就行了,关键在于性能。   数据量很大百万条记录,因此考虑到要用多线程并发执行,在写的过程中又遇到问题,我想统计所有子进程执行完......
  • 《asyncio 系列》7. 在 asyncio 中引入多线程
    楔子在从头开始开发新的IO密集型应用程序时,asyncio可能是首选技术,并且也要使用与asyncio搭配工作的非阻塞库,如asyncpg、aiohttp等等。然而我们工作的很大一部分可能是使用阻塞IO库管理现有的代码,例如对HTTP发请求的requests,用于PostgreSQL数据库的psycopg2,或其他......
  • 特性介绍 | MySQL 测试框架 MTR 系列教程(二):进阶篇 - 内存/线程/代码覆盖率/单元/压力
    作者:卢文双资深数据库内核研发序言:以前对MySQL测试框架MTR的使用,主要集中于SQL正确性验证。近期由于工作需要,深入了解了MTR的方方面面,发现MTR的能力不仅限于此,还支持单元测试、压力测试、代码覆盖率测试、内存错误检测、线程竞争与死锁等功能,因此,本着分享的精神,将其......
  • 存储双活同步导致数据库异常恢复---惜分飞
    联系:手机/微信(+8617813235971)QQ(107644445)标题:存储双活同步导致数据库异常恢复作者:惜分飞©版权所有[未经本人同意,不得以任何形式转载,否则有进一步追究法律责任的权利.]客户双活存储异常之后,单个存储运行,故障存储修复之后,双活同步,出现多套系统异常,上一篇:Controlf......
  • c# 多线程编程
    涉及的类Thread//用于手动创建线程ThreadPool//线程池System.Threading.CancellationTokenSource//用于取消线程池线程Monitor//线程同步线程(Thread)与进程当我们打开一个应用程序后,操作系统就会为该应用程序分配一个进程ID。进程可以理解为一块包含了某些资源的内......