多线程-为什么一个线程会完成所有工作? [英] Multithreading--Why one thread is doing all of the work?

查看:79
本文介绍了多线程-为什么一个线程会完成所有工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用两个线程将两个矩阵相乘(但是,程序也是按比例缩放编写的,因此我可以使用三个,四个等线程来代替).每个线程为最终矩阵的一行(或一列)计算/执行功.如果一个线程在一行上进行工作,则其他线程不应在该行上工作.它/他们应该继续前进到下一个可用行.

I am multiplying two matrices using two threads (however, the program is written to scale up as well, so I could possibly use three, four, etc threads instead). Each thread calculates/does the work for one row (or column) of the final matrix. If one thread is doing work on a row, the other one(s) should not work on that row. It/they should move on to the next available row.

首先,我不确定实现问题的方式是否正确.如果您能找到更好的方法,请告诉我.

First of all, I am not certain if the way I implemented the problem is correct. If you can see a better way, please let me know.

其次,我的测试方式是,每次测试(使用不同大小的矩阵,甚至是巨大的矩阵)时,只有一个线程可以完成工作.也就是说,每次相同的线程都可以访问run()方法的同步块.其他线程正在进入run()方法,但是为什么只有一个线程总是获得锁并完成所有工作?

Secondly, the way I have done it, every time I test it (with different size matrices--even huge ones), only one thread does the work. That is, each time, the same thread is getting access to the synchronized block of the run() method. The other threads are entering the run() method, but why is only one thread always gaining the lock and doing all of the work?

这是我的运行方法:

 public void run() {
    System.out.println(Thread.currentThread().getName());
    while (i < number of columns in final matrix) {
        synchronized (this) {
            if (i < number of columns in final matrix) {
                for (int j = 0; j < Main.B[0].length; j++) { 
                    for (int k = 0; k < Main.A[0].length; k++) { 
                        Main.C[i][j] += Main.A[i][k] * Main.B[k][j];
                    }
                }
                i++;
            }
        }
    }
} 

这是我的驱动程序类中的代码,用于创建线程并启动程序:

This is the code in my driver class that creates the threads and starts the program:

MyRunnable r = new MyRunnable();
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();

try {
    thread1.join();
    thread2.join();
    } catch (InterruptedException ie) {
        System.out.println("\nThe following error occurred: " + ie);
        }
    }

我想我的问题有两个方面-我的方法是否正确解决了眼前的问题?如果是这样(如果不是),为什么一个线程总是抓住锁并完成所有工作?我已经在20x20矩阵上检查了最多6个线程的程序,并且始终只有一个线程在执行该工作.

I guess my question is two-fold--is my approach correct for the problem at hand? If so, (and if not), why is one thread always grabbing the lock and doing all of the work? I have checked the program with up to 6 threads on 20x20 matrices and always only one thread is doing the work.

推荐答案

正如一些评论所建议的那样,问题出在锁定(即synchronized(this)部分)上.同步是在this上完成的,在您的情况下,该实例是MyRunnable的单个实例,因此,当一个线程在synchronized块内进行工作时,所有其他线程将等待直到工作完成.如此有效,一次只有一个线程在做真实的工作.

As some of the comments suggested, the problem is in the locking (i.e. the synchronized(this) part). Synchronizing is done on this which, in your case, a single instance of MyRunnable, so while one thread is doing the work inside the synchronized block, all other threads will wait until the work is finished. So effectively, only one thread is doing real work at a time.

这是解决问题的方法.由于您需要线程在并行的不同行上工作,因此该工作必须通过锁进行同步(因为锁定意味着相反的情况:一次只能有一个线程可以完成该工作).您要做的 要同步的是每个线程决定它将在哪一行上工作的部分.

Here's how to solve the problem. Since you need your threads to work on different rows in parallel, then this work must not be synchronized by a lock (because locking means the opposite: only one thread can do the work at a time). What you do need to synchronize is the part where each thread decides which row it will work on.

这是一个示例伪代码:

public void run(){
  int workRow;
  synchronized(this){
    workRow = findNextUnprosessedRow();
  }
  for(int i=0; i<matrix[workRow].length; i++){
    //do the work
  }
}

请注意,由于上述原因,实际工作有意地同步.

Note that the actual work is intentionally not synchronized, for reasons given above.

您使用线程的方式是正确的,因此没有问题,但是,我建议您看看Java的并发API:

The way you are using threads is correct, so there is not problem with that, however, I would suggest you have a look at Java's concurrency API: Thread Pools. Here's an example of how to use it in your context:

//Creates a pool of 5 concurrent thread workers
ExecutorService es = Executores.newFixedThreadPool(5);

//List of results for each row computation task
List<Future<Void>> results = new ArrayList<Future<Void>>();
try{
  for(int row=0; row<matrix.length; row++){
    final int workRow = row;

    //The main part. You can submit Callable or Runnable
    // tasks to the ExecutorService, and it will run them
    // for you in the number of threads you have allocated.
    // If you put more than 5 tasks, they will just patiently
    // wait for a task to finish and release a thread, then run.
    Future<Void> task = es.submit(new Callable<Void>(){
      @Override
      public Void call(){
        for(int col=0; col<matrix[workRow].length; col++){
          //do something for each column of workRow
        }
        return null;
      }
    });
    //Store the work task in the list.
    results.add(task);
  }
}finally{
  //Make sure thread-pool is shutdown and all worker
  //threads are released. 
  es.shutdown();
}

for(Future<Void> task : results){
  try{
    //This will wait for threads to finish. 
    // i.e. same as Thread.join()
    task.get();
  }catch(ExecutionException e){
    //One of the tasks threw an exception!
    throw new RuntimeException(e);
  }
}

这种方法更清洁,因为主要完成工作分配 线程(外部for循环),因此无需对其进行同步.

This approach is a lot cleaner, because the work distribution is done the main thread (the outer for-loop), and therefore there is no need to synchronize it.

使用线程池时,您还将获得很少的奖励:

You also get few bonuses when working with thread pools:

  • 它很好地处理了每个计算过程中的所有异常 的线程.当使用裸线程时,就像您的方法一样,这很容易 丢掉"异常.

  • It nicely takes care of any exceptions during the computations in each of the threads. When working with bare threads, like in your approach, it is easy to "lose" an exception.

线程被池化.也就是说,它们会自动重用,因此您无需担心产生新线程的成本.这在您的情况下特别有用,因为您可能需要在矩阵的每一行产生一个线程,我怀疑这可能相当大.

Threads are pooled. That is, they get automatically reused so you don't need to worry about the cost of spawning new threads. This is particularly useful in your case, since you will need to spawn a thread per row in your matrix, which may be fairly large, I suspect.

提交给ExecutorService的任务被包装在一个有用的Future<Result>对象中,该对象在每个计算任务实际上返回某种结果时最有用.在您的情况下,如果您需要对矩阵中的所有值求和,那么每个计算任务都可以返回该行的总和.然后,您只需要总结一下.

Tasks submitted to ExecutorService are wrapped in a useful Future<Result> object, which is most useful when each computation task actually returns some kind of result. In your case, if you needed to sum-up all values in the matrix, then each computation task could return the sum for the row. Then you'd just need to sum up those up.

花了一点时间,但希望它能清除一些东西.

Got a bit long, but hope it clears some things up.

这篇关于多线程-为什么一个线程会完成所有工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆