【操作系统作业】数独解决方案验证器(利用多线程解决)

简介: 【操作系统作业】数独解决方案验证器(利用多线程解决)

一、题目

数独谜题使用 9×9 的网格,其中每一列和每一行以及每 3×3 子网格中的每

一个子网格必须包含所有数字 1···9。 图 1 给出了一个有效的数独游戏示例。

这个项目包括设计多线程应用程序来确定数独谜题的解决是否有效。

这个多线程应用程序有几种不同的设计。一种建议的策略是创建检查以下条

件的线程:


一个线程,检查每列包含数字 1 到 9

一个线程,检查每行包含数字 1 到 9

9个线程来检查 3×3 子网格中的每个子网格是否包含数字 1 到 9这总共创建 11 个单独的线程,以验证数独谜题。但是,也可以为该项目创建更多线程。例如,不是创建一个线程来检查9列,而是创建9个线程来分别检查每列。


将参数传给每个线程

父线程创建工作线程,向每个工作线程传递它在数独网格中检查的位置。这

一步需要向每个线程传递多个参数。最简单的方法是:采用 struct 创建一个数

据结构。例如,为了线程的验证,可用包括行和列的数据结构来传递参数。

/* structure for passing data to threads */ 
typedef struct
{
int row;
int column;
} parameters;
Pthreads 和 Windows 程序的工作线程创建类似如下代码所示:
parameters *data = (parameters *) malloc(sizeof(parameters));
data->row = 1;
data->column = 1;
/* Now create the thread passing it data as a parameter */

指针 data 将被传递给线程创建函数 pthread create((Pthreads)或CreateThread()(Windows)函数,后者将把 data 作为参数传递到作为单独线程运行的函数。

将结果返回到父线程

每个工作线程都被分配了一项任务,即判定数独谜题中特定区域的有效性。工作进程执行此检查后,必须将其结果传给父进程。处理这个问题的一个好方法是:创建一个对每个线程可见的整数值数组。此数组中的第 i 个索引对应于第 i个工作线程。如果一个工作线程将其对应的值设置为 1,则表示它的数独谜题区域是有效的;为 0 的值表示无效。当所有工作线程完成后,父线程检查结果数组中的每项,确定数独游戏是否有效。


二、设计思路

用Java的多线程来并发执行,利用了jdk中的CountDownLatch计数器和ThreadPoolExecutor线程池来解决问题。


思路:9个线程执行9个区块的检查,另外两个分别负责行列的检查,同时将这些操作放入到线程池,并利用CountDownLatch来统计线程的情况,当所有线程完成时,主线程mian输出结果数组。


三、代码

这里判断时我用了一个小技巧来加快检查效率,即利用flag数组存放某个数字被标记的次数,如果此次检查时,该数字i对应的flag[i]中标记次数等于轮数+1,则表示在该轮检查中,已经被标记过了一次,此次又遇到,说明此轮中存在两个相同数字,与规则相悖,直接记录结果,返回即可(别忘了把计数器减一表示线程已经结束)。


package com.dreamchaser.concurrent;
import java.util.Arrays;
import java.util.concurrent.*;
/**
 * @author 金昊霖
 */
public class Main {
    public static void main(String[] args) {
        // 线程数
        final int THREAD_POOL_SIZE = 11;
        //0-8表示9个区块,9,10表示行,列检查。值为0则通过,1表示不通过
        int [] result=new int[11];
        int [][]numbers={
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
                {6,2,4,5,3,9,1,8,7},
        };
        //一次性计数器,用于协调多线程,这里的主要目的在于等待所有线程结束再输出结果
        final  CountDownLatch countDown = new CountDownLatch(11);
        // 创建线程池,其中任务队列需要结合实际情况设置合理的容量
        ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_POOL_SIZE,
                THREAD_POOL_SIZE,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024),
                new ThreadPoolExecutor.AbortPolicy());
        executor.execute(new Runnable() {
            //检查行
            @Override
            public void run() {
                int [] flag=new int[9];
                for (int i=0;i<9;i++){
                    for (int j=0;j<9;j++){
                        //如果是i+1则说明不符合条件,标记并退出即可
                        if (flag[numbers[i][j]-1]==i+1){
                            result[9]=1;
                            countDown.countDown();
                            return;
                        }
                        flag[numbers[i][j]-1]++;
                    }
                }
                countDown.countDown();
            }
        });
        executor.execute(new Runnable() {
            //检查列
            @Override
            public void run() {
                int [] flag=new int[9];
                for (int i=0;i<9;i++){
                    for (int j=0;j<9;j++){
                        //如果是i+1则说明不符合条件,标记并退出即可
                        if (flag[numbers[j][i]-1]==i+1){
                            result[9]=1;
                            countDown.countDown();
                            return;
                        }
                        flag[numbers[j][i]-1]++;
                    }
                }
                countDown.countDown();
            }
        });
        for (int i=0;i<9;i+=3){
            for (int j=0;j<9;j+=3){
                executor.execute(new Check(i,j,result,numbers,countDown));
            }
        }
        System.out.println("未等待所有线程结束的结果:"+Arrays.toString(result));
        try {
            //等待其他所有线程结束
            countDown.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有线程结束后的结果:"+Arrays.toString(result));
    }
}
/**
 * 线程类,用于检查9个区块
 */
class Check implements Runnable{
    int i;
    int j;
    int[] result;
    int[][] numbers;
    CountDownLatch countDownLatch;
    public Check(int i, int j, int[] result, int[][] numbers,CountDownLatch countDownLatch) {
        this.i = i;
        this.j = j;
        this.result = result;
        this.numbers = numbers;
        this.countDownLatch=countDownLatch;
    }
    @Override
    public void run() {
        int [] flag=new int[9];
        for (int x=i;x<i+3;x++){
            for (int y=j;y<j+3;y++){
                if (flag[numbers[x][y]-1]==1){
                    result[i+j/3]=1;
                    countDownLatch.countDown();
                    return;
                }
                flag[numbers[x][y]-1]++;
            }
        }
        countDownLatch.countDown();
    }
}


四、总结

我之前其实自学过Java多线程的知识,可现在实际用起来还是得重新回去查资料,看来看一遍只是说我了解了这个知识,甚至只是知道这个知识,至于如何去用还需多多实践,只有在不断实践中才能理解掌握。


相关文章
|
2月前
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
在数字世界的底层,操作系统扮演着不可或缺的角色。它如同一位高效的管家,协调和控制着计算机硬件与软件资源。本文将拨开迷雾,深入探索操作系统中两个核心概念——进程与线程。我们将从它们的诞生谈起,逐步剖析它们的本质、区别以及如何影响我们日常使用的应用程序性能。通过简单的比喻,我们将理解这些看似抽象的概念,并学会如何在编程实践中高效利用进程与线程。准备好跟随我一起,揭开操作系统的神秘面纱,让我们的代码运行得更加流畅吧!
|
6月前
|
UED 开发者 Python
探索操作系统的心脏:理解进程与线程
【8月更文挑战第31天】在数字世界的海洋中,操作系统犹如一艘巨轮,其稳定航行依赖于精密的进程与线程机制。本文将揭开这一机制的神秘面纱,通过深入浅出的语言和直观的代码示例,引领读者从理论到实践,体验进程与线程的魅力。我们将从基础概念出发,逐步深入到它们之间的联系与区别,最后探讨如何在编程实践中高效运用这些知识。无论你是初学者还是有经验的开发者,这篇文章都将为你的技术之旅增添新的航标。
|
3月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
2月前
|
算法 调度 开发者
深入理解操作系统:进程与线程的管理
在数字世界的复杂编织中,操作系统如同一位精明的指挥家,协调着每一个音符的奏响。本篇文章将带领读者穿越操作系统的幕后,探索进程与线程管理的奥秘。从进程的诞生到线程的舞蹈,我们将一起见证这场微观世界的华丽变奏。通过深入浅出的解释和生动的比喻,本文旨在揭示操作系统如何高效地处理多任务,确保系统的稳定性和效率。让我们一起跟随代码的步伐,走进操作系统的内心世界。
|
3月前
|
安全 Java 开发者
Java多线程编程中的常见问题与解决方案
本文深入探讨了Java多线程编程中常见的问题,包括线程安全问题、死锁、竞态条件等,并提供了相应的解决策略。文章首先介绍了多线程的基础知识,随后详细分析了每个问题的产生原因和典型场景,最后提出了实用的解决方案,旨在帮助开发者提高多线程程序的稳定性和性能。
|
3月前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
3月前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
54 2
|
3月前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
5月前
|
存储 消息中间件 资源调度
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
该文章总结了操作系统基础知识中的十个关键知识点,涵盖了进程与线程的概念及区别、进程间通信方式、线程同步机制、死锁现象及其预防方法、进程状态等内容,并通过具体实例帮助理解这些概念。
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
|
4月前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
55 1