4 votes

Fils synchronisés pour la multiplication de matrices

J'ai fait des recherches sur ce sujet toute la nuit et je n'ai pas trouvé de solution. Si quelqu'un peut m'aider, j'apprécierais vraiment ! Je suis probablement en train de rater quelque chose de super évident. C'est un devoir pour comprendre la synchronisation où nous reprenons un devoir précédent où nous avons utilisé des threads pour multiplier 2 matrices. Dans le devoir précédent, chaque thread multipliait une ligne, il y avait donc autant de threads que de lignes.

Dans ce travail, nous ne sommes censés utiliser que 5 threads - tous les threads sont censés commencer par une ligne/colonne et une fois que le thread est terminé, il doit choisir la prochaine ligne/colonne disponible en utilisant la synchronisation, de sorte que deux threads vont se retrouver à faire la même colonne.

Cette question a contribué à me mettre sur la bonne voie, mais je dois faire quelque chose de mal avec l'implémentation, car jusqu'à présent, je n'ai réussi à faire fonctionner le programme que de deux façons :

  1. n'effectue que les 5 premières lignes - les 5 threads s'exécutent une fois, chacun d'entre eux calculant une ligne ou un groupe de lignes.
  2. J'ai ajouté une boucle (qui est maintenant commentée dans mon code) pour que le fil continue à fonctionner, mais lorsque je fais cela, seul le premier fil effectue un travail.

Voici ma classe avec ma méthode principale et quelques méthodes d'aide :

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;

public class MatrixMult {

public static void main(String[] args){
    int[][] matrixA;
    int[][] matrixB;
    int colA = 0;
    int rowA = 0;
    int colB = 0;
    int rowB = 0;
    Scanner userInput = new Scanner( System.in );
    System.out.println("Please enter the dimensions of matrix A");

    do{
        System.out.print("column for matrix A: ");
        colA = userInput.nextInt();
        System.out.println();
    } while(!validDimension(colA));

    rowB = colA;

    do{
        System.out.print("row for matrix A: ");
        rowA = userInput.nextInt();
        System.out.println();
    } while(!validDimension(rowA));

    matrixA = new int[rowA][colA];

    System.out.println("Please enter the dimensions of matrix B:");
    do{
        System.out.print("column for matrix B: ");
        colB = userInput.nextInt();
        System.out.println();
    } while(!validDimension(colB));

    matrixB = new int[rowB][colB];

    fillMatrix(matrixA);
    fillMatrix(matrixB);

    System.out.println("Would you like to print out matrix A and B? (y/n)");
    String userResponse = userInput.next();
    if(userResponse.equalsIgnoreCase("y")){
        System.out.println("Matrix A:");
        printBackMatrix(matrixA);
        System.out.println();
        System.out.println("Matrix B:");
        printBackMatrix(matrixB);
        System.out.println();
    }

    int[][] matrixProduct3 = multMatrixWithThreadsSync(matrixA, matrixB);

    String fileName = "C:/matrix.txt";
    System.out.println("Matrix product is being written to "+fileName);
    try {
        printMatrixToFile(matrixProduct3, fileName);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

private static int[][] multMatrixWithThreadsSync(int[][] matrixA, int[][] matrixB) {

    int[][] matrixProduct = new int[matrixA.length][matrixB[0].length];
    int[] matrixProductColumn = new int[matrixA.length];

    Runnable task = new MultMatrixByRow(matrixA, matrixB, matrixProduct);

    for(int i=0; i<5; i++){

        Thread worker = new Thread(task);
        worker.start();
//          System.out.println(worker.getName());
        try {
            worker.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    return matrixProduct;
}

private static void printMatrixToFile(int[][] matrix, String fileName) throws IOException{
    PrintWriter userOutput = new PrintWriter(new FileWriter(fileName));
    for(int i=0; i<matrix.length; i++){
        for(int j=0; j<matrix[0].length; j++){
            userOutput.print(matrix[i][j]+" ");
        }
        userOutput.println();
    }
    userOutput.close();

}

private static void printBackMatrix(int[][] matrix) {
    for(int i=0; i<matrix.length; i++){
        for(int j=0; j<matrix[0].length; j++){
            System.out.print(matrix[i][j]+" ");
        }
        System.out.println();
    }
}

private static void fillMatrix(int[][] matrix) {
    Random rand = new Random();

    for(int i=0; i<matrix.length; i++){
        for(int j=0; j<matrix[0].length; j++){
            matrix[i][j] = rand.nextInt(100) + 1;
        }
    }

}

public static boolean validDimension(int dim){
    if (dim <= 0 || dim >1000){
        System.err.println("Dimension value entered is not valid");
        return false;
    }
    return true;

}
}

et voici ma classe avec runnable :

public class MultMatrixByRow implements Runnable {
private int i;
private int[][] matrixA;
private int[][] matrixB;
private int[][] matrixProduct;

public MultMatrixByRow(int[][] A, int[][] B, int[][] C) {
    this.matrixA = A;
    this.matrixB = B;
    this.matrixProduct = C;
}

@Override   
public void run(){
//      while(i < matrixProduct.length){
        int rowToWork = 0;
        synchronized (this){
 //             System.out.println("i is "+i);
            if ( i < matrixProduct.length){
                rowToWork = i;
                i++;
            }
            else{
                return;
            }
        }
        for(int j = 0; j < matrixB[0].length; j++){
            for(int k=0; k < matrixA[0].length; k++){
                matrixProduct[rowToWork][j] += matrixA[rowToWork][k]*matrixB[k][j];
            }
        }
//      }
        }
    }

Encore une fois, toute aide serait vraiment appréciée ! Merci beaucoup.

3voto

Rastax Points 300
  1. Vous ne synchronisez pas sur une ressource, vous devez partager un objet verrou (dans un contexte statique ou par constructeur).
  2. Je n'arrive pas vraiment à comprendre ce qui dans votre programme devrait être synchronisé alors que vous ne les laissez même pas travailler de manière synchrone..... Vous démarrez un thread et attendez directement qu'il s'arrête. Je pense que vous devez les démarrer tous d'abord et ensuite dans une autre boucle appeler le join sur chaque thread.

De plus, je ne suis pas tout à fait sûr de ce que vos fils doivent traiter séparément, je pense qu'ils traitent tous la matrice du produit entier. Vous devez partager une variable utilisée pour identifier les lignes déjà traitées, à laquelle vous accédez de manière synchronisée.

Je pourrais corriger votre code mais j'apprécierais que vous fassiez ce travail vous-même, puisque c'est une tâche de comprendre la concurrence des fils.

EDIT : Explication du terme "synchronisé" :
La synchronisation prend un objet comme verrou, dont un seul thread peut détenir le contrôle. Lorsqu'il a le moniteur pour le verrou, le thread peut traiter le bloc, sinon, il doit attendre pour obtenir le moniteur.
Dans votre cas, vous pourriez utiliser private static final Object lock = new Object(); comme verrou, vous vous synchroniserez sur.

EDIT 2 : J'ai complètement construit votre code
Je ne suis pas très fier d'avoir fait tout le travail, mais peu importe, le voici.

package anything.synchronize_stackoverflow_post;

/**
 * @date 21.11.2012
 * @author Thomas Jahoda
 */
public class ConcurrentMatrixMultiplyingTask implements Runnable {

    private int[][] matrixA;
    private int[][] matrixB;
    private int[][] matrixProduct;
    //
    private final ConcurrencyContext context;

    public ConcurrentMatrixMultiplyingTask(ConcurrencyContext context, int[][] A, int[][] B, int[][] C) {
        if (context == null) {
            throw new IllegalArgumentException("context can not be null");
        }
        this.context = context;
        this.matrixA = A;
        this.matrixB = B;
        this.matrixProduct = C;
    }

    @Override
    public void run() {
        while (true) {
            int row;
            synchronized (context) {
                if (context.isFullyProcessed()) {
                    break;
                }
                row = context.nextRowNum();
            }
            System.out.println(Thread.currentThread().getName() + " is going to process row " + row);
            // i'm not really sure if this matrix algorithm here is right, idk..
            for (int j = 0; j < matrixB[0].length; j++) {
                for (int k = 0; k < matrixA[0].length; k++) {
                    matrixProduct[row][j] += matrixA[row][k] * matrixB[k][j];
                }
            }
        }
    }

    public static class ConcurrencyContext {

        private final int rowCount;
        private int nextRow = 0;

        public ConcurrencyContext(int rowCount) {
            this.rowCount = rowCount;
        }

        public synchronized int nextRowNum() {
            if (isFullyProcessed()) {
                throw new IllegalStateException("Already fully processed");
            }
            return nextRow++;
        }

        public synchronized boolean isFullyProcessed() {
            return nextRow == rowCount;
        }
    }
}

Et la tâche de traitement

package anything.synchronize_stackoverflow_post;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @date 21.11.2012
 * @author Thomas Jahoda
 */
public class MatrixMulti {

    public static void main(String[] args) {
        int[][] matrixA;
        int[][] matrixB;
        int colA = 0;
        int rowA = 0;
        int colB = 0;
        int rowB = 0;
        Scanner userInput = new Scanner(System.in);
        System.out.println("Please enter the dimensions of matrix A");

        do {
            System.out.print("column for matrix A: ");
            colA = userInput.nextInt();
            System.out.println();
        } while (!validDimension(colA));

        rowB = colA;

        do {
            System.out.print("row for matrix A: ");
            rowA = userInput.nextInt();
            System.out.println();
        } while (!validDimension(rowA));

        matrixA = new int[rowA][colA];

        System.out.println("Please enter the dimensions of matrix B:");
        do {
            System.out.print("column for matrix B: ");
            colB = userInput.nextInt();
            System.out.println();
        } while (!validDimension(colB));

        matrixB = new int[rowB][colB];

        fillMatrix(matrixA);
        fillMatrix(matrixB);

        System.out.println("Would you like to print out matrix A and B? (y/n)");
        String userResponse = userInput.next();
        if (userResponse.equalsIgnoreCase("y")) {
            System.out.println("Matrix A:");
            printBackMatrix(matrixA);
            System.out.println();
            System.out.println("Matrix B:");
            printBackMatrix(matrixB);
            System.out.println();
        }

        int[][] matrixProduct3 = multMatrixWithThreadsSync(matrixA, matrixB);

        String fileName = "test.txt";
        System.out.println("Matrix product is being written to " + fileName);
        try {
            printMatrixToFile(matrixProduct3, fileName);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private static int[][] multMatrixWithThreadsSync(int[][] matrixA, int[][] matrixB) {

        int[][] matrixProduct = new int[matrixA.length][matrixB[0].length];
        int[] matrixProductColumn = new int[matrixA.length];
        //
        ConcurrentMatrixMultiplyingTask.ConcurrencyContext context = new ConcurrentMatrixMultiplyingTask.ConcurrencyContext(matrixProduct.length);
        //
        Runnable task = new ConcurrentMatrixMultiplyingTask(context, matrixA, matrixB, matrixProduct);
        Thread[] workers = new Thread[5];
        for (int i = 0; i < workers.length; i++) {
            workers[i] = new Thread(task, "Worker-"+i);
        }
        for (int i = 0; i < workers.length; i++) {
            Thread worker = workers[i];
            worker.start();
        }
        for (int i = 0; i < workers.length; i++) {
            Thread worker = workers[i];
            try {
                worker.join();
            } catch (InterruptedException ex) {
                Logger.getLogger(MatrixMulti.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return matrixProduct;
    }

    private static void printMatrixToFile(int[][] matrix, String fileName) throws IOException {
        PrintWriter userOutput = new PrintWriter(new FileWriter(fileName));
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                userOutput.print(matrix[i][j] + " ");
            }
            userOutput.println();
        }
        userOutput.close();

    }

    private static void printBackMatrix(int[][] matrix) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }

    private static void fillMatrix(int[][] matrix) {
        Random rand = new Random();

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                matrix[i][j] = rand.nextInt(100) + 1;
            }
        }

    }

    public static boolean validDimension(int dim) {
        if (dim <= 0 || dim > 1000) {
            System.err.println("Dimension value entered is not valid");
            return false;
        }
        return true;

    }
}

1voto

maasg Points 4359

Pour aborder votre problème, vous devez définir ce qui constitue une "unité de travail". Cette "unité de travail" (ou tâche) est ce que chaque thread va exécuter. Après avoir défini cela, vous pouvez raisonner sur ce dont cette unité de travail a besoin pour effectuer son travail.

Dans le cas de la multiplication de matrices, l'unité naturelle de travail est chaque cellule de la matrice résultante. Ainsi, étant donné les matrices A[i,j] et B[j,k], votre calcul pourrait se concentrer sur le produit scalaire du vecteur A.row(x) (point) B.column(y) pour chaque cellule de la matrice. (0<=x<i,0<=y<k) .

L'étape suivante consiste à représenter chaque tâche. Une structure idéale pour "alimenter" les tâches aux threads est une file d'attente. java.util.concurrent.BlockingQueue est un tel exemple, où le travail de synchronisation est effectué sous le capot. Étant donné qu'on vous demande de raisonner sur la synchronisation "à la main", vous pouvez utiliser un autre conteneur, comme une liste (ou même un tableau). Votre structure contiendra chaque cellule qui définit la matrice résultante. Cela pourrait être quelque chose comme ceci :

class Cell;  // int x, int y, getters, setters, ...
// build the structure that contains the work to be shared
List<Cell> cells = new LinkedList<Cell>();
for (int i=0;i<a.rows;i++) {
   for (int j=0;j<b.columns;j++) {
       cells.add(new Cell(i,j)); // represent the cells of my result matrix
   }
}

Maintenant, vous avez besoin d'une tâche qui, étant donné une cellule et les matrices A et B, peut calculer la valeur de cette cellule. C'est votre unité de travail et donc ce qui s'exécute dans le contexte d'un thread. Ici, vous devez également décider si vous voulez que le résultat soit placé. En java, vous pourriez utiliser des futures et assembler votre matrice en dehors du contexte du thread, mais pour garder les choses simples, je partage un tableau qui contiendra les résultats. (Comme, par définition, il n'y aura pas de collisions)

class DotProduct implements Runnable {
 int[][] a;
 int[][] b;
 int[][] result; 
 List<Cell> cells;
 public DotProduct(int[][] a, int[][] b, int[][]result, List<Cell> cells) {
 ...
 }
 public void run() {
     while(true) {
         Cell cell = null;
         synchronized(cells) { // here, we ensure exclusive access to the shared mutable structure
             if (cells.isEmpty()) return; // when there're no more cells, we are done.
             Cell cell = cells.get(0); // get the first cell not calculated yet
             cells.remove(cell);  // remove it, so nobody else will work on it
         }
         int x = cell.getX();
         int y = cell.getY();
         z = a.row(x) (dot) b.column(y);
         synchronized (result) {
             result[x][y] = z;
         }
     }
}

Maintenant, vous avez presque terminé. La seule chose qu'il vous reste à faire est de créer les fils, de les "alimenter" avec la commande DotProduct et attendez qu'ils aient terminé. Notez que j'ai synchronisé sur result pour mettre à jour la matrice de résultats. Bien que, par définition, il n'y ait aucune chance d'accès concurrent à une même cellule (car chaque thread travaille sur une cellule différente), vous devez vous assurer que le résultat est "publié en toute sécurité" pour les autres threads en synchronisant explicitement l'objet. Ceci peut également être fait en déclarant result volatile mais je ne suis pas sûr que vous ayez déjà couvert ce terrain.

J'espère que cela vous aidera à comprendre comment aborder un problème de concurrence.

0voto

Alexei Kaigorodov Points 5841

Vous utilisez tout le spectre des primitives de synchronisation : Sémaphore, Lock, synchronisé. Il vaut mieux commencer avec synchronized seulement, pour apprendre les choses. Ce dont vous avez besoin est une ressource qui indique la prochaine ligne/colonne à traiter, si elle existe. Tous les threads y accèdent en utilisant un bloc synchronisé, lisent la ligne/colonne suivante, déplacent la ligne/colonne vers la cellule suivante, sortent du bloc et traitent la ligne/colonne obtenue.

Si la fin de la matrice est atteinte, le fil de travail se termine simplement. Le thread principal attend que tous les threads de travail quittent avec Thread.join().

0voto

UmNyobe Points 9508

Vous avez vraiment mal compris la réponse à votre question précédente. rowToWork doit être partagée entre les fils. Et un thread doit probablement appeler une méthode à la construction pour obtenir sa valeur initiale. Vous devez comprendre que votre Section critique est l'attribution de la ligne suivante à un fil donné.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X