C/C++ Threads): Creating worker threads that will be listening to jobs and executing them concurrent...

本文探讨了使用两个线程来处理连续到达的任务的方法,并通过C++实现了一个简单的例子。作者希望在任务到达时解锁线程进行处理,但在实践中遇到了线程同步的问题。目前的代码在Visual Studio中运行时会触发错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Suppose we have two workers. Each worker has an id of 0 and 1. Also suppose that we have jobs arriving all the time, each job has also an identifier 0 or 1 which specifies which worker will have to do this job.

I would like to create 2 threads that are initially locked, and then when two jobs arrive, unlock them, each of them does their job and then lock them again until other jobs arrive.

I have the following code:

  #include <iostream>
  #include <thread>
  #include <mutex>

  using namespace std;

  struct job{

      thread jobThread;
      mutex jobMutex;

  };

  job jobs[2];


  void executeJob(int worker){

      while(true){

          jobs[worker].jobMutex.lock();

          //do some job

      }

   }

  void initialize(){

      int i;
      for(i=0;i<2;i++){
                jobs[i].jobThread = thread(executeJob, i);
      }

   }

  int main(void){

      //initialization
      initialize();

      int buffer[2];
      int bufferSize = 0;

      while(true){
          //jobs arrive here constantly, 
            //once the buffer becomes full, 
            //we unlock the threads(workers) and they start working
          bufferSize = 2;
          if(bufferSize == 2){
              for(int i = 0; i<2; i++){
                  jobs[i].jobMutex.unlock();
              }
          }
           break;
     }

  }

I started using std::thread a few days ago and I'm not sure why but Visual Studio gives me an error saying abort() has been called. I believe there's something missing however due to my ignorance I can't figure out what.

I would expect this piece of code to actually

  1. Initialize the two threads and then lock them

  2. Inside the main function unlock the two threads, the two threads will do their job(in this case nothing) and then they will become locked again.

But it gives me an error instead. What am I doing wrong?

Thank you in advance!

package com.example.demo.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.thread.ThreadFactoryBuilder; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WHostProcess; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.request.ProcessRequest; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WHostProcessService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.interceptor.TransactionAspectSupport; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Autowired private WHostProcessService wHostProcessService; @Data private static class UserSession { private Process cmdProcess; private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; private final Queue<String> logBuffer = new ConcurrentLinkedQueue<>(); private static final int BATCH_SIZE = 50; // 批量写入阈值 // 命令执行状态锁 private final AtomicBoolean isExecuting = new AtomicBoolean(false); } private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; // 异步日志写入线程池 private static final ExecutorService LOG_WRITER_POOL = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNamePrefix("log-writer-").build()); // 日志刷新调度器 private static final ScheduledExecutorService LOG_FLUSH_SCHEDULER = Executors.newSingleThreadScheduledExecutor(); public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; startLogFlushService(); } // 初始化日志刷新服务 private void startLogFlushService() { LOG_FLUSH_SCHEDULER.scheduleAtFixedRate(() -> { userSessions.values().forEach(session -> { if (!session.getLogBuffer().isEmpty()) { List<String> batch = CollUtil.newArrayList(session.getLogBuffer()); session.getLogBuffer().clear(); asyncWriteLog(session.getLogFilePath(), String.join("\n", batch)); } }); }, 0, 1, TimeUnit.SECONDS); } @Override public void executeCommand(String command, String host, String userId) { // 0. ABORT命令特殊处理(优先处理终止请求) if ("ABORT".equalsIgnoreCase(command)) { handleAbort(userId); return; } // 1. 权限校验 if (!validateUserHost(userId, host)) { sendError("无权访问该主机", userId); return; } // 2. 检查用户当前会话状态 UserSession session = userSessions.get(userId); if (session != null && session.isExecuting.get()) { sendError("已有命令执行中,请等待完成或使用ABORT终止", userId); return; } // 3. 创建新会话(带原子状态检查) session = userSessions.computeIfAbsent(userId, key -> { UserSession newSession = createNewSession(userId, host); if (newSession != null) { newSession.isExecuting.set(true); // 标记为执行中 } return newSession; }); if (session == null) return; // 4. 写入日志并执行命令 try { // 确保获得执行锁 if (!session.isExecuting.compareAndSet(true, true)) { sendError("命令执行冲突,请重试", userId); return; } session.getLogBuffer().offer("——————————————— " + DateUtil.now() + " ———————————————"); // 发送命令到进程 IoUtil.write(session.getCmdProcess().getOutputStream(), Charset.forName("gbk"), true, command + "\n"); } catch (Exception e) { session.isExecuting.set(false); // 发生异常时释放锁 sendError("命令发送失败: " + e.getMessage(), userId); } } @Override public Boolean isCommandExecuting(String userId) { UserSession session = userSessions.get(userId); return session != null && session.isExecuting.get(); } @Override public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session == null || session.getCmdProcess() == null) { sendError("没有活动的命令进程", userId); return; } try { long pid = session.getCmdProcess().pid(); System.out.println("Attempting to kill process with PID: " + pid); // 使用 taskkill 命令终止进程 ProcessBuilder taskKill = new ProcessBuilder("taskkill", "/F", "/T", "/PID", String.valueOf(pid)); Process killProcess = taskKill.start(); // 等待命令执行完成 int exitCode = killProcess.waitFor(); System.out.println("taskkill exit code: " + exitCode); if (exitCode == 0) { // 进程终止成功 session.isExecuting.set(false); cleanupSession(userId); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "✔️" + "进程已通过 taskkill 终止 (PID: " + pid + ")"); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, ""); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, System.getProperty("user.dir") + ">"); } else { // 进程终止失败 sendError("终止进程失败,错误码: " + exitCode, userId); } } catch (IOException | InterruptedException e) { System.err.println("Error killing process: " + e.getMessage()); sendError("终止进程失败: " + e.getMessage(), userId); } } @Override public String startProcess(ProcessRequest processRequest) { try { // 数据库表新增数据 String p13 = processRequest.getP13().trim(); String processName = processRequest.getProcessName().trim(); String productNumber = processRequest.getProductNumber().trim(); String executeHost = processRequest.getExecuteHost().trim(); String department = processRequest.getDepartment().trim(); String version = processRequest.getVersion().trim(); if (StrUtil.isEmpty(p13) || StrUtil.isEmpty(processName) || StrUtil.isEmpty(productNumber) || StrUtil.isEmpty(executeHost) || StrUtil.isEmpty(department) || StrUtil.isEmpty(version)) { return "新增进程失败。"; } WHostProcess wHostProcess = new WHostProcess(); wHostProcess.setP13(p13); wHostProcess.setProcessName(processName); wHostProcess.setProductNumber(productNumber); wHostProcess.setHost(executeHost); wHostProcess.setPid(null); wHostProcess.setDepartment(department); wHostProcess.setState("离线"); wHostProcess.setVersion(version); wHostProcess.setBeginTime(new Date()); boolean saveResult = wHostProcessService.save(wHostProcess); if (saveResult) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper .eq(WPersonalHost::getExecuteHost, processRequest.getExecuteHost()) .eq(WPersonalHost::getSharedHost, processRequest.getSharedHost()); WPersonalHost wPersonalHost = wPersonalHostService.getOne(queryWrapper); // 执行py启动命令 //todo 后续动态 String pythonEXEPath = "D:\\miniforge\\envs" + File.separator + p13 + File.separator + "python" + wPersonalHost.getPythonEnv() + File.separator + "python.exe"; String mainPyPath = System.getProperty("user.dir") + File.separator + "python-package" + File.separator + executeHost + File.separator + p13 + File.separator + "test" + File.separator + "main.py"; executeCommand(pythonEXEPath + " " + mainPyPath, processRequest.getExecuteHost(), processRequest.getP13()); return "正在启动项目..."; } } catch (Exception e) { e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return "新增进程失败。"; } private boolean validateUserHost(String userId, String executeHost) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); return wPersonalHostService.getOne(queryWrapper) != null; } private UserSession createNewSession(String userId, String executeHost) { try { UserSession session = new UserSession(); session.setSessionId(UUID.randomUUID().toString()); session.setLogFilePath(initLogFile(userId, executeHost)); // 启动CMD进程(带唯一标题) ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/k", "title " + generateUniqueTitle(userId)); session.setCmdProcess(pb.redirectErrorStream(true).start()); // 启动输出监听线程 startOutputThread(session, userId); return session; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } } private String initLogFile(String userId, String executeHost) { // 1. 构建基础路径(使用File.separator) String baseDir = FileUtil.normalize( System.getProperty("user.dir") + File.separator + "command-log"); // 2. 构建安全路径(统一使用File.separator) String safePath = FileUtil.normalize( baseDir + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"); // 3. 安全校验(现在路径分隔符一致) if (!safePath.startsWith(baseDir)) { throw new SecurityException("非法日志路径: " + safePath); } // 4. 创建目录(自动处理路径分隔符) FileUtil.mkdir(safePath); // 5. 生成日志文件 String logFileName = DateUtil.today() + ".log"; return FileUtil.touch(safePath + File.separator + logFileName).getAbsolutePath(); } private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), "GBK"))) { String line; while ((line = reader.readLine()) != null) { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); session.getLogBuffer().offer(line); } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { session.isExecuting.set(false); // 命令结束释放锁 cleanupSession(userId); // 通知前端命令执行结束 messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "⏹ 该命令执行已结束"); } }).start(); } private void asyncWriteLog(String logFilePath, String content) { CompletableFuture.runAsync(() -> { try { // 替换掉日志文件中没用的信息,如多余的路径信息 String currentDir = System.getProperty("user.dir"); String escapedDir = currentDir.replace("\\", "\\\\"); String regex = "(?m)^\\s*" + escapedDir + ">(?!\\S)\\s*"; // 创建 Pattern 和 Matcher 对象 Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); // 检查是否存在匹配的模式 if (matcher.find()) { // 如果存在匹配,则进行替换 String cleaned = content.replaceAll(regex, ""); FileUtil.appendString(cleaned + System.lineSeparator(), logFilePath, "GBK"); } else { FileUtil.appendString(content + System.lineSeparator(), logFilePath, "GBK"); } } catch (Exception e) { System.err.println("日志写入失败: " + e.getMessage()); } }, LOG_WRITER_POOL); } private void cleanupSession(String userId) { UserSession session = userSessions.remove(userId); if (session != null) { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } // 强制将剩余日志写入文件(新增代码) if (!session.getLogBuffer().isEmpty()) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } catch (Exception ignored) { } } } @PreDestroy public void cleanup() { LOG_FLUSH_SCHEDULER.shutdown(); LOG_WRITER_POOL.shutdown(); userSessions.forEach((userId, session) -> { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (Exception ignored) { } }); userSessions.clear(); } /** * 发送错误日志 */ private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌" + message); } catch (Exception ignored) { } } /** * 生成cmd窗口唯一id */ private String generateUniqueTitle(String userId) { return "CMD_SESSION_" + userId + "_" + System.currentTimeMillis(); } }我有个问题,为什么我执行main.py文件#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 输出1到10的Python脚本 """ for i in range(1, 11): print(i)。输出的数字不是按照顺序的,是凌乱的1 2 10 5 3 7 4 8 9 6 但如果我用本地的cmd窗口,执行,就是正常的
最新发布
06-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值