`

第二章 - 管理多个线程 - Executors

阅读更多

Executor框架相比于传统的并发系统基础实现具有很多的优势。传统做法是实现一个Runnable接口的类,然后使用该类的对象来直接创建Thread实例。

这种做法有一些问题,特别是当你启动太多线程的时候,你可能降低了整个系统的性能。

 

Executor 框架里的基础组件

  • Executor 接口:这是 executor 框架里的基本组件。它只定义了一个允许程序员给executor发送Runnable对象的方法。
  • ExecutorService 接口:这个接口继承了 Executor 接口,并包含了额外一些方法来增加框架的功能,其中包括:
  1.           运行一些具有返回值的任务:Runnable 接口提供的 run() 方法没有返回值,但是使用 executors,你可以运行一些有返回值的任务
  2.           使用一个方法来执行一系列的任务
  3.           完成 executor 的执行并等待其销毁
  • ThreadPoolExecutor 类:这个类实现了 Executor 和 ExecutorService 两个接口。另外,它提供了一些其它方法来获取 executor 的状态(工作线程数,已经被执行的任务数等等),配置 executor(最小和最大工作线程数)。
  • Executors 类:该类提供了一些实用方法用以创建 Executor 对象以及其它相关的类。

 本章提供了几个例子来解释Executor的使用。例子代码有点长,不过相对还是蛮简单的清晰的。

 

实例1 - K 邻近算法(K-nearest neighbors algorithm)

K邻近算法是一个用于监督分类的简单机器学习算法。算法包含以下主要部分:

  • 训练数据集:该数据集由一系列样本组成,每个样本具由一个或多个属性,还有一个特殊属性来记录每个样本的标签
  • 一个距离矩阵:这个矩阵用来计算需要分类的样本和训练数据集里样本的距离
  • 测试数据集:该数据集用来衡量算法的行为。

当要对一个样本进行归类时,算法计算该样本和训练数据集里所有样本的距离。然后再取距离最小的的 k 个样本,这 k 个样本中,哪个标签数最多,那么这个标签就赋给要归类的那个样本。根据第一章得出的经验,我们从算法的串行版本开始,然后从串行版本演变到并行版本。

 

K邻近算法 - 串行版本

 

public class KnnClassifier {
    private List<? extends Sample> dataSet;
    private int k;

    public KnnClassifier(List<? extends Sample> dataSet, int k) {
        this.dataSet = dataSet;
        this.k = k;
    }

    public String classify(Sample example) {
        Distance[] distances = new Distance[dataSet.size()];
        int index = 0;

        // 计算新样本和训练数据集中各样本之间的距离
        for (Sample localExample : dataSet) {
            distances[index] = new Distance();
            distances[index].setIndex(index);
            distances[index].setDistance
                    (EuclideanDistanceCalculator.calculate(localExample,
                            example));
            index++;
        }

        // 对计算得到的距离排序以便获取K个最近距离的样本
        Arrays.sort(distances);

        Map<String, Integer> results = new HashMap<>();
        for (int i = 0; i < k; i++) {
            Sample localExample = dataSet.get(distances[i].getIndex());
            String tag = localExample.getTag();
            results.merge(tag, 1, (a, b) -> a + b);
        }

        // 返回最近k个样本总数最多的那个标签
        return Collections.max(results.entrySet(),
                Map.Entry.comparingByValue()).getKey();
    }
}
 

 

 

// 该类用来计算两个样本的距离
public class EuclideanDistanceCalculator {
    public static double calculate (Sample example1, Sample
            example2) {
        double ret=0.0d;
        double[] data1=example1.getExample();
        double[] data2=example2.getExample();
        if (data1.length!=data2.length) {
            throw new IllegalArgumentException ("Vector doesn't have the
                    same length");
        }
        for (int i=0; i<data1.length; i++) {
            ret+=Math.pow(data1[i]-data2[i], 2);
        }
        return Math.sqrt(ret);
    }
}
 

 

K邻近算法 - 细颗粒度的并发版本

如果你分析以上的算法的并行版本,你会发现有两点你可以用并行来实现:

  • 距离的计算:计算输入的样本和训练数据集中各样本的距离的循环中,每一个循环都是独立的,他们之间并不互相依赖。
  • 距离的排序:Java 8 提供了 Arrays 类的 parallelSort() 方法来并发实现数据排序

在细颗粒度并发版本中,我们为每一个计算输入样本和训练数据集中样本的距离创建一个任务。由此可见,所谓的细颗粒度就是我们创建了很多的任务。

 

 

 

public class KnnClassifierParallelIndividual {
    private List<? extends Sample> dataSet;
    private int k;
    private ThreadPoolExecutor executor;
    private int numThreads;
    private boolean parallelSort;

    public KnnClassifierParallelIndividual(List<? extends Sample>
                                                   dataSet, int k, 
                                                   int factor, 
                                                   boolean parallelSort) {
        this.dataSet = dataSet;
        this.k = k;

        // 动态获取运行此程序的处理器或核的数量来决定线程池中线程的数量
        numThreads = factor * (Runtime.getRuntime().availableProcessors());
        executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(numThreads);
        this.parallelSort = parallelSort;
    }

    /**
     * 因为我们为每个距离计算创建了一个任务,因此主线程需要等待所有任务完成后才能继续,
     * 我们使用 CountDownLatch 这个类来同步所有任务的完成,
     * 我们用任务总数也就是数据集中样本的总数来初始化 CountDownLatch,
     * 每个任务完成后调用 countDown() 方法
     */
    public String classify(Sample example) throws Exception {
        Distance[] distances = new Distance[dataSet.size()];
        CountDownLatch endController = new CountDownLatch(dataSet.size());
        int index = 0;
        for (Sample localExample : dataSet) {
            IndividualDistanceTask task = new
                    IndividualDistanceTask(distances, index, localExample,
                    example, endController);
            executor.execute(task);
            index++;
        }
        endController.await();

        if (parallelSort) {
            Arrays.parallelSort(distances);
        } else {
            Arrays.sort(distances);
        }

        Map<String, Integer> results = new HashMap<>();
        for (int i = 0; i < k; i++) {
            Sample localExample = dataSet.get(distances[i].getIndex());
            String tag = localExample.getTag();
            results.merge(tag, 1, (a, b) -> a + b);
        }

        // 返回最近k个样本总数最多的那个标签
        return Collections.max(results.entrySet(),
                Map.Entry.comparingByValue()).getKey();
    }

    public void destroy() {
        executor.shutdown();
    }
}
 

 

 

public class IndividualDistanceTask implements Runnable {
    private Distance[] distances;
    private int index;
    private Sample localExample;
    private Sample example;
    private CountDownLatch endController;

    public IndividualDistanceTask(Distance[] distances, int index,
                                  Sample localExample,
                                  Sample example, CountDownLatch endController) {
        this.distances = distances;
        this.index = index;
        this.localExample = localExample;
        this.example = example;
        this.endController = endController;
    }

    public void run() {
        distances[index] = new Distance();
        distances[index].setIndex(index);
        distances[index].setDistance
                (EuclideanDistanceCalculator.calculate(localExample,
                        example));

        // 任务完成,调用CountDownLatch的countDown()
        endController.countDown();
    }
}
 

 

K 邻近算法 - 粗颗粒度的并发算法版本

细颗粒度版本的问题是创建了太多的任务,粗颗粒度版本中,我们让每一个任务处理数据集的一个子集,这样避免创建太多的任务。

public class KnnClassifierParallelIndividual {
    private List<? extends Sample> dataSet;
    private int k;
    private ThreadPoolExecutor executor;
    private int numThreads;
    private boolean parallelSort;

    public KnnClassifierParallelIndividual(List<? extends Sample>
                                                   dataSet, int k, int factor, boolean parallelSort) {
        this.dataSet = dataSet;
        this.k = k;

        // 动态获取运行此程序的处理器或核的数量来决定线程池中线程的数量
        numThreads = factor * (Runtime.getRuntime().availableProcessors());
        executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numThreads);
        this.parallelSort = parallelSort;
    }

    /**
     * 因为我们为每个距离计算创建了一个任务,因此主线程需要等待所有任务完成后才能继续,
     * 我们使用 CountDownLatch 这个类来同步所有任务的完成,
     * 我们用任务总数也就是数据集中样本的总数来初始化 CountDownLatch,每个任务完成后调用 countDown() 方法
     */
    public String classify(Sample example) throws Exception {
        Distance distances[] = new Distance[dataSet.size()];
        CountDownLatch endController = new CountDownLatch(numThreads);
        int length = dataSet.size() / numThreads;
        int startIndex = 0, endIndex = length;

        for (int i = 0; i < numThreads; i++) {
            GroupDistanceTask task = new GroupDistanceTask(distances,
                    startIndex, endIndex, dataSet, example, endController);
            startIndex = endIndex;
            if (i < numThreads - 2) {
                endIndex = endIndex + length;
            } else {
                endIndex = dataSet.size();
            }
            executor.execute(task);
        }

        endController.await();

        if (parallelSort) {
            Arrays.parallelSort(distances);
        } else {
            Arrays.sort(distances);
        }

        Map<String, Integer> results = new HashMap<>();
        for (int i = 0; i < k; i++) {
            Sample localExample = dataSet.get(distances[i].getIndex());
            String tag = localExample.getTag();
            results.merge(tag, 1, (a, b) -> a + b);
        }

        // 返回最近k个样本总数最多的那个标签
        return Collections.max(results.entrySet(),
                Map.Entry.comparingByValue()).getKey();
    }

    public void destroy() {
        executor.shutdown();
    }
}

 

public class GroupDistanceTask implements Runnable {
    private Distance[] distances;
    private int startIndex, endIndex;
    private Sample example;
    private List<? extends Sample> dataSet;
    private CountDownLatch endController;

    public GroupDistanceTask(Distance[] distances, int startIndex,
                             int endIndex, List<? extends Sample> dataSet, Sample
                                     example, CountDownLatch endController) {
        this.distances = distances;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
        this.example = example;
        this.dataSet = dataSet;
        this.endController = endController;
    }

    public void run() {
        for (int index = startIndex; index < endIndex; index++) {
            Sample localExample = dataSet.get(index);
            distances[index] = new Distance();
            distances[index].setIndex(index);
            distances[index].setDistance(EuclideanDistanceCalculator
                    .calculate(localExample, example));
        }
        endController.countDown();
    }
}

 

实例2 - 并行在客户端/服务器端架构中的应用

在这个例子中:

  • 客户端和服务器端通过sockets通信
  • 客户端向服务器端发送查询以及其它请求,请求以字符串的形式发送,服务器端接收到请求后处理请求并返回处理结果
  • 服务器端接受以下不同请求:
  1. Query:该类型请求格式为 q; codCountry; codIndicator; year
  2. Report: 该类型请求格式为 r; codIndicator
  3. Stop: 该请求格式为 z
  4. 其它请求类型,服务器返回错误信息

跟上述例子一样,我们先从串行版本入手然后过渡到并行版本。

 

客户端/服务器端 - 串行版本

程序包括以下三个主要部分:

  • DAO部分,负责访问数据并且获得客户查询的结果 (我们在代码中忽略DAO的代码,因为它在本例中不是重点)
  • 指令(command)部分,每一种请求类型对应相应的指令(command)类
  • 服务器部分,它接收查询,调用相应的指令并返回查询结果给客户

以下是串行版本指令类部分的代码

//*****************串行版本指令类部分*****************//

// 指令的抽象类
public abstract class Command {
    protected String[] command;
    public Command (String [] command) {
        this.command=command;
    }
    public abstract String execute ();
}

// 对应Query请求的指令类
public class QueryCommand extends Command {
    public QueryCommand(String [] command) {
        super(command);
    }

    public String execute() {
        WDIDAO dao=WDIDAO.getDAO();
        if (command.length==3) {
            return dao.query(command[1], command[2]);
        } else if (command.length==4) {
            try {
                return dao.query(command[1], command[2],
                        Short.parseShort(command[3]));
            } catch (Exception e) {
                return "ERROR;Bad Command";
            }
        } else {
            return "ERROR;Bad Command";
        }
    }
}

//对应Report请求的指令类
public class ReportCommand extends Command {
    public ReportCommand(String [] command) {
        super(command);
    }

    public String execute() {
        WDIDAO dao=WDIDAO.getDAO();
        return dao.report(command[1]);
    }
}

//对应Stop请求的指令类
public class StopCommand extends Command {
    public StopCommand(String [] command) {
        super(command);
    }

    public String execute() {
        return "Server stopped";
    }
}

//此类处理一些服务器不支持的请求
public class ErrorCommand extends Command {
    public ErrorCommand(String [] command) {
        super(command);
    }

    public String execute() {
        return "Unknown command: "+command[0];
    }
}

 

以下是服务器部分的代码

 

public class SerialServer {
    public static void main(String[] args) throws IOException {
        boolean stopServer = false;
        System.out.println("Initialization completed.");
        try (ServerSocket serverSocket = new ServerSocket(Constants.SERIAL_PORT)) {
            // 不断循环,直到stopServer被设置为false
            do {
                try (Socket clientSocket = serverSocket.accept();
                     PrintWriter out = new PrintWriter
                             (clientSocket.getOutputStream(), true);
                     BufferedReader in = new BufferedReader(new
                             InputStreamReader(clientSocket.getInputStream()));) {
                    String line = in.readLine();
                    Command command;
                    String[] commandData = line.split(";");
                    System.out.println("Command: " + commandData[0]);
                    switch (commandData[0]) {
                        case "q":
                            System.out.println("Query");
                            command = new QueryCommand(commandData);
                            break;
                        case "r":
                            System.out.println("Report");
                            command = new ReportCommand(commandData);
                            break;
                        case "z":
                            System.out.println("Stop");
                            command = new StopCommand(commandData);
                            stopServer = true;
                            break;
                        default:
                            System.out.println("Error");
                            command = new ErrorCommand(commandData);
                    }
                    String response = command.execute();
                    System.out.println(response);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } while (!stopServer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

 

客户端/服务器端 - 并行版本

众所周知以上串行版本存在着严重的性能问题,服务器一次只能处理一个请求,其余的请求需要等待。并行版本中,我们将改为主线程接收请求,然后为每个请求创建一个任务,并交由线程池中的线程执行。

 

以下是并行版本指令类部分的代码,大部分代码和并行版本一样,除了Stop指令类。类名我们改为以"Concurrent"开始

 

//*****************串行版本指令类部分*****************//

// 指令的抽象类
public abstract class Command {
    protected String[] command;
    public Command (String [] command) {
        this.command=command;
    }
    public abstract String execute ();
}

// 对应Query请求的指令类
public class ConcurrentQueryCommand extends Command {
    public ConcurrentQueryCommand(String [] command) {
        super(command);
    }

    public String execute() {
        WDIDAO dao=WDIDAO.getDAO();
        if (command.length==3) {
            return dao.query(command[1], command[2]);
        } else if (command.length==4) {
            try {
                return dao.query(command[1], command[2],
                        Short.parseShort(command[3]));
            } catch (Exception e) {
                return "ERROR;Bad Command";
            }
        } else {
            return "ERROR;Bad Command";
        }
    }
}

//对应Report请求的指令类
public class ConcurrentReportCommand extends Command {
    public ConcurrentReportCommand(String [] command) {
        super(command);
    }

    public String execute() {
        WDIDAO dao=WDIDAO.getDAO();
        return dao.report(command[1]);
    }
}

//对应Stop请求的指令类
public class ConcurrentStopCommand extends Command {
    public ConcurrentStopCommand(String [] command) {
        super(command);
    }

    public String execute() {
        ConcurrentServer.shutdown();
        return "Server stopped";
    }
}

//此类处理一些服务器不支持的请求
public class ConcurrentErrorCommand extends Command {
    public ConcurrentErrorCommand(String [] command) {
        super(command);
    }

    public String execute() {
        return "Unknown command: "+command[0];
    }
}

//并行版本中新增了服务器状态查询指令
public class ConcurrentStatusCommand extends Command {
    public ConcurrentStatusCommand (String[] command) {
        super(command);
    }
    
    @Override
    public String execute() {
        StringBuilder sb=new StringBuilder();
        ThreadPoolExecutor executor=ConcurrentServer.getExecutor();
        sb.append("Server Status;");
        sb.append("Actived Threads: ");
        sb.append(String.valueOf(executor.getActiveCount()));
        sb.append(";");
        sb.append("Maximum Pool Size: ");
        sb.append(String.valueOf(executor.getMaximumPoolSize()));
        sb.append(";");
        sb.append("Core Pool Size: ");
        sb.append(String.valueOf(executor.getCorePoolSize()));
        sb.append(";");
        sb.append("Pool Size: ");
        sb.append(String.valueOf(executor.getPoolSize()));
        sb.append(";");
        sb.append("Largest Pool Size: ");
        sb.append(String.valueOf(executor.getLargestPoolSize()));
        sb.append(";");
        sb.append("Completed Task Count: ");
        sb.append(String.valueOf(executor.getCompletedTaskCount()));
        sb.append(";");
        sb.append("Task Count: ");
        sb.append(String.valueOf(executor.getTaskCount()));
        sb.append(";");
        sb.append("Queue Size: ");
        sb.append(String.valueOf(executor.getQueue().size()));
        sb.append(";");
        return sb.toString();
    }
}

 

 

 以下是服务器部分代码和实现Runnable接口的RequestTask类

public class ConcurrentServer {
    private static ThreadPoolExecutor executor;
    private static ServerSocket serverSocket;
    private static volatile boolean stopped = false;

    public static void main(String[] args) throws InterruptedException {
        serverSocket = null;
        executor = (ThreadPoolExecutor) Executors.newFixedThreadPool
                (Runtime.getRuntime().availableProcessors());
        System.out.println("Initialization completed.");
        serverSocket = new ServerSocket(Constants.CONCURRENT_PORT);

        do {
            try {
                Socket clientSocket = serverSocket.accept();
                RequestTask task = new RequestTask(clientSocket);
                executor.execute(task);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } while (!stopped);

        executor.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("Shutting down cache");
        System.out.println("Cache ok");
        System.out.println("Main server thread ended");
    }

    public static void shutdown() {
        stopped = true;
        System.out.println("Shutting down the server...");
        System.out.println("Shutting down executor");
        executor.shutdown();
        System.out.println("Executor ok");
        System.out.println("Closing socket");
        try {
            serverSocket.close();
            System.out.println("Socket ok");
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Shutting down logger");
        System.out.println("Logger ok");
    }

    public static ThreadPoolExecutor getExecutor() {
        return executor;
    }
}

public class RequestTask implements Runnable {
    private Socket clientSocket;
    public RequestTask(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    public void run() {
        try (PrintWriter out = new
                PrintWriter(clientSocket.getOutputStream(),
                true);
             BufferedReader in = new BufferedReader(new
                     InputStreamReader(
                     clientSocket.getInputStream()));) {
            String line = in.readLine();

            Command command;
            String[] commandData = line.split(";");
            System.out.println("Command: " + commandData[0]);
            switch (commandData[0]) {
                case "q":
                    System.err.println("Query");
                    command = new ConcurrentQueryCommand(commandData);
                    break;
                case "r":
                    System.err.println("Report");
                    command = new ConcurrentReportCommand(commandData);
                    break;
                case "s":
                    System.err.println("Status");
                    command = new ConcurrentStatusCommand(commandData);
                    break;
                case "z":
                    System.err.println("Stop");
                    command = new ConcurrentStopCommand(commandData);
                    break;
                default:
                    System.err.println("Error");
                    command = new ConcurrentErrorCommand(commandData);
                    break;
            }
            ret = command.execute();

            System.out.println(ret);
            out.println(ret);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

其它有用的方法

Executors 类提供了另外一些方法创建 ThreadPoolExecutor 对象。这些方法包括:

  • newCachedThreadPool():这个方法创建了一个 ThreadPoolExecutor 对象, 这种线程池能够重复利用那些空闲的工作线程,但是如果有需要,会创建新的工作线程。因此它没有最大的工作线程数。
  • newSingleThreadExecutor():这个方法创建了一个只有一个工作线程的 ThreadPoolExecutor 对象。发送给executor的任务保存在队列中,一一被那个工作线程执行。
  • CountDownLatch类提供了以下额外的方法: await(long timeout, TimeUnit unit) 当前线程等待直到内部计数器降为0;如果等待时间超过了参数 timeout,该方法返回 false。getCount() 该方法返回内部计数器的值。

Java中支持两种并行数据结构:

  • 阻塞数据结构 (Blocking data structures):此类数据结构如果不能满足请求的操作,将阻塞访问的线程直到请求能被满足(例如取数据请求但数据结构中无数据)
  • 非阻塞数据结构 (Non-blocking data structures):与阻塞数据结构不同,该数据结构无法满足请求时并不阻塞访问线程

有些数据结构实现了两种行为,有些数据结构则只实现一种行为。通常,阻塞数据结构同时也实现具有非阻塞行为的方法,但是非阻塞线程没有实现阻塞行为的方法。

 

阻塞操作的方法有:

  • put(), putFirst(), putLast():插入数据到数据结构,如果数据结构已满,则阻塞访问线程直到数据结构有可用空间时
  • take(), takeFirst(), takeLast():返回并删除数据结构中的数据。如果数据结构为空,则阻塞访问线程直到数据结构有一个元素

非阻塞操作的方法有:

  • add(), addFirst(), addLast():插入数据到数据结构,如果数据结构已满,则抛出 IllegalStateException 异常
  • remove(), removeFirst(), removeLast():返回并删除数据结构中的数据。如果数据结构为空,则抛出 IllegalStateException 异常
  • element(), getFirst(), getLast():返回但不删除数据。如果数据结构为空,则抛出 IllegalStateException 异常
  • offer(), offerFirst(), offerLast():插入数据到数据结构,如果数据结构已满,则返回 false
  • poll(), pollFirst(), pollLast():返回并删除数据结构中的数据。如果数据结构为空,则返回 null
  • peek(), peekFirst(), peekLast():返回但不删除数据。如果数据结构为空,则返回 null
分享到:
评论

相关推荐

    第9章多线程第9章多线程第9章多线程第9章多线程第9章多线程第9章多线程

    为了防止多个线程同时访问共享资源导致数据不一致,Java提供了多种同步机制,如`synchronized`关键字、`wait()`、`notify()`和`notifyAll()`方法,以及`java.util.concurrent`包中的高级同步工具,如`Lock`和`...

    JAVA高质量并发详解,多线程并发深入讲解

    - **Semaphore:** 信号量,用于控制多个线程对共享资源的访问。 #### 二、高级特性和设计模式 - **线程池:** - **ExecutorService:** 提供了一种管理线程的方法,可以控制线程的并发数量,简化线程的管理和...

    day19_阻塞队列、线程池、File类、递归.pdf

    ### 第二章:线程池 线程池(Thread Pool)是Java中通过`ExecutorService`和`ThreadPoolExecutor`实现的。它解决了频繁创建和销毁线程带来的资源浪费问题,提高了系统的效率和响应速度。线程池的基本工作流程是: ...

    呼伦贝尔市-扎兰屯市-街道行政区划_150783_Shp数据-wgs84坐标系.rar

    呼伦贝尔市-扎兰屯市-街道行政区划_150783_Shp数据-wgs84坐标系.rar

    text13届真题二.zip

    text13届真题二.zip

    锡林郭勒盟-东乌珠穆沁旗-街道行政区划_152525_Shp数据-wgs84坐标系.rar

    街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用

    WPF实现工业级动态流体管道动画:C#代码解析与性能优化

    内容概要:本文详细介绍了如何使用WPF(Windows Presentation Foundation)实现逼真的工业组态软件中的流体管道动画。主要内容涵盖管道绘制、流体动画效果、动态速度控制以及性能优化等方面。首先,通过C#代码展示了如何使用几何图形和颜色动画创建动态变化的管道。接着,引入粒子系统和模糊效果来增强流体的真实感。为了实现流体速度的动态调整,文中提供了流速控制器的实现方法。此外,还讨论了基于帧刷新的性能优化技术和双重缓冲机制的应用。最后,文章提到了一些高级技巧,如Perlin噪声生成流速波动、粒子沿曲线运动、动态纹理等。 适合人群:对WPF开发感兴趣的中级及以上水平的开发者,尤其是那些希望深入了解WPF图形和动画特性的程序员。 使用场景及目标:适用于需要开发工业组态软件或其他涉及流体模拟应用的项目。主要目标是帮助开发者掌握如何使用WPF创建高效且视觉效果出色的流体动画。 其他说明:文中提供的代码片段可以直接应用于实际项目中,同时也鼓励读者进一步探索更多复杂的流体模拟技术。

    HCIA-Datacom高阶:vlan、vlanif、单臂路由、静态路由、ospf综合实验

    HCIA-Datacom高阶:vlan、vlanif、单臂路由、静态路由、ospf综合实验

    毕业论文 基于fpga的rs 232串口通讯逻辑设计说明书.doc

    毕业论文 基于fpga的rs 232串口通讯逻辑设计说明书.doc

    呼伦贝尔市-阿荣旗-街道行政区划_150721_Shp数据-wgs84坐标系.rar

    呼伦贝尔市-阿荣旗-街道行政区划_150721_Shp数据-wgs84坐标系.rar

    微电网能源管理中的随机博弈与Python实现:基于双网络架构的动态定价与负荷调度

    内容概要:本文详细介绍了微电网中能源管理的随机博弈模型及其Python实现。首先,通过构建MicrogridEnv类来模拟多方博弈环境,每个智能体可以进行买卖操作并调整负荷。接着,引入了ET网络用于处理价格博弈,ADL网络用于负荷预测。这两个网络通过策略梯度协同优化,共同实现动态定价和负载调度。文中展示了具体的训练过程和实验结果,证明了该模型在波动环境下能够显著提高系统收益稳定性。此外,还讨论了动态定价策略的具体实现,包括供需平衡系数计算和价格波动修正项的设计。最后,通过多智能体交互代码展示了真实的博弈过程,并进行了对比实验,验证了模型的有效性和优越性。 适合人群:对微电网能源管理和强化学习感兴趣的科研人员、工程师和技术爱好者。 使用场景及目标:适用于研究和开发微电网能源管理系统,旨在通过动态定价和负荷调度优化能源利用效率,提高系统收益和稳定性。 其他说明:本文不仅提供了详细的代码实现,还深入探讨了模型背后的理论依据和设计思路,帮助读者全面理解微电网能源管理中的随机博弈机制。

    皮秒分辨率的FPGA TDC技术研究.pdf

    皮秒分辨率的FPGA TDC技术研究.pdf

    【Java Web开发】Tomcat服务器配置与优化:面试专题及性能调优详解Tomcat服务器的

    内容概要:本文档《Tomcat面试专题及答案.pdf》详细介绍了Tomcat服务器的相关知识点,涵盖配置、优化、部署、内存与垃圾回收调优、Session处理、JMS远程监控、专业分析工具、Session数目查看、内存使用情况监视、类加载与对象回收情况打印以及Tomcat的工作模式。文档首先讲解了Tomcat的默认端口及修改方法,随后深入探讨了四种Connector运行模式(bio、nio、aio、apr)及其参数配置。接着介绍了三种Web应用部署方式,并阐述了Tomcat容器创建Servlet实例的原理。在优化部分,重点讨论了连接配置、内存调优、垃圾回收策略的选择,还涉及了共享Session的多种处理方案。最后,文档概述了一个HTTP请求在Tomcat内部的完整处理流程。 适合人群:有一定Java开发经验,特别是Web开发背景的研发人员和技术专家。 使用场景及目标:①准备技术面试,尤其是针对Tomcat相关问题;②优化现有基于Tomcat的应用系统性能;③深入了解Tomcat架构及其工作原理,以更好地进行应用部署和维护。 其他说明:文档内容详实,既适合初学者入门学习,也适合有一定经验的开发者深入研究。建议读者在实际工作中结合自身环境进行针对性配置与优化实践。

    软考中级-软件设计师知识点整理(一篇就过(3).html

    软考中级-软件设计师知识点整理(一篇就过(3).html

    MATLAB数据预测:融合多种机器学习与统计模型的时间序列预测方法

    内容概要:本文详细介绍了使用MATLAB进行数据预测的各种方法和技术细节,涵盖了现代的人工智能算法如LSTM、BP神经网络、RBF和Elman等,以及传统的统计方法如ARIMA和GM灰色预测。文中不仅提供了具体的代码实例,还分享了许多实用的经验和注意事项,强调了数据预处理的重要性。作者通过多个实际案例展示了不同算法在不同数据集上的表现差异,指出了选择合适算法的关键在于理解数据本身的特性。 适合人群:对时间序列预测感兴趣的科研人员、工程师以及有一定编程基础并希望深入理解MATLAB预测工具的学生。 使用场景及目标:适用于需要进行时间序列数据分析和预测的研究项目,旨在帮助读者掌握如何根据具体应用场景选择最合适的预测模型,并能够独立完成从数据准备到模型评估的全过程。 其他说明:文章特别提醒读者,在面对复杂多变的实际问题时,除了关注算法本身外,更要重视数据的质量和预处理步骤。此外,作者还提供了一些关于模型调优的小贴士,如调整LSTM层数、设置ARIMA参数等。

    沧州市-吴桥县--街道行政区划_130928_Shp-wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接使用。

    流水线贴膜机:基于PLC与触摸屏的工业自动化控制及运动控制初学者指南

    内容概要:本文详细介绍了流水线贴膜机的控制系统设计,涵盖PLC与触摸屏的协同控制。具体包括上下气缸、夹紧气缸、输送带电机、贴膜伺服和旋转电机的控制逻辑。PLC程序实现了各部件的协调运作,而触摸屏提供了友好操作界面。文中不仅展示了完整的程序结构和关键代码片段,还分享了许多实际调试经验和常见问题解决方案。 适合人群:对工业自动化控制感兴趣的初学者,尤其是想要深入了解PLC编程和运动控制的技术人员。 使用场景及目标:适用于学习PLC编程、触摸屏设计、气缸和电机控制、伺服定位等基础知识。通过该项目,学习者可以掌握工业自动化系统的完整开发流程,理解各组件间的协作机制,并积累实际调试经验。 其他说明:项目支持博图V15.1及以上版本,强调模块化设计和良好的代码规范,有助于提高程序的可维护性和扩展性。文中提供的实例和技巧能够帮助初学者更好地理解和应用工业自动化控制技术。

    工业级激光雷达SLAM三维建图:基于点云算法与高精度云台系统的创新应用

    内容概要:本文详细介绍了自主研发的工业级三维扫描系统,该系统利用二维激光雷达与高精度单轴云台相结合,实现了高效、精准的三维点云建模。文章重点阐述了云台控制、数据同步、点云重建、滤波算法以及多雷达适配等方面的技术细节。云台控制系统采用裸机驱动程序,确保角度定位误差小于0.03度;数据同步方面,通过时间戳双缓冲机制和优化的时间对齐算法,提高了数据处理速度;点云重建部分,提出了改进的坐标转换矩阵,显著提升了重建精度;针对工业环境的特点,开发了多种滤波算法,有效去除噪点;此外,系统支持多种雷达的动态配置,增强了灵活性和适应性。 适合人群:从事激光雷达SLAM研究、三维建图、工业自动化领域的研究人员和技术人员。 使用场景及目标:适用于矿山、冶金、建筑等复杂工业环境中的三维数据获取和建模任务,旨在提高测绘效率和精度,降低设备成本,增强系统的鲁棒性和可靠性。 其他说明:文中提供了大量的代码片段和实际应用场景案例,强调了技术创新和实用性的结合,展现了从硬件设计到软件算法的全面解决方案。

    海洋气候与海洋生物数据集

    观测日期 位置 海洋位置名称(例如,马尔代夫大堡礁) 纬度 观测点纬度 经度 观测点经度 海温(°C) 海面温度(摄氏度) pH值 海水的酸度(较低意味着酸性更强,这是酸化的标志) 漂白严重程度 分类变量:无、低、中、高 观察到的物种 采样期间观察到的海洋物种数量 海洋热浪 布尔标志(真/假),指示SST是否>30°C 随着气候变化的加速,世界海洋正在经历重大变革。该数据集汇编了海面温度(SST)、pH值、珊瑚白化严重程度和生态关键海洋区物种观测的合成但真实的测量结果。它涵盖了2015年至2023年,模拟了海洋环境如何应对全球变暖、酸化和热浪。 该数据集的目标是支持机器学习、气候分析和生态建模

    邢台市-信都区--街道行政区划_130503_Shp-wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接下载使用。

Global site tag (gtag.js) - Google Analytics