Простые потоки не выполняются точно параллельно - PullRequest
0 голосов
/ 31 декабря 2018

Я запускаю десять потоков, чтобы обновить десять индикаторов выполнения.
Для завершения панелей требуется пять секунд.
При запуске только одного потока он завершается через пять секунд.
При запуске всех десятипотоки, они обновляются асинхронно и завершаются более чем за пять секунд.
Возможно ли выполнить эту задачу более эффективным образом, чтобы все десять индикаторов выполнения выполнялись за пять секунд?

Вот MainActivity.java

package com.google.example;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    ArrayList<ProgressBar> bars = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for(int i = 0; i < 10; i++){
            int id = getResources().getIdentifier("b" + (i+1), "id", getPackageName());
            bars.add((ProgressBar) findViewById(id));
        }

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                int num = 10;

                for(int i = 0; i < num; i++){
                    MyRunnable runnable = new MyRunnable(bars.get(i));
                    Thread thread = new Thread(runnable);
                    thread.start();
                }

            }
        });

    }

    class MyRunnable implements Runnable{

        ProgressBar bar;

        MyRunnable(ProgressBar b){
            bar = b;
        }

        @Override
        public void run() {
            int progress = 0;
            long previous = 0;

            while(progress < 100) {
                long time = System.currentTimeMillis();
                if (time != previous && time % 50 == 0) {
                    progress++;
                    bar.setProgress(progress);
                    previous = time;
                }
            }

        }
    }
}

А вот активность_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start"/>

    <ProgressBar
        android:id="@+id/b1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b6"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b7"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b8"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b9"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

    <ProgressBar
        android:id="@+id/b10"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"/>

</LinearLayout> 

Вот вывод при запуске только одного потока

enter image description here

Вот при запуске все вместе

enter image description here

РЕДАКТИРОВАТЬ: Как предложено @ greeble31, я решилпроблема, изменив MyRunnable на

class MyRunnable implements Runnable{

        ProgressBar bar;

        MyRunnable(ProgressBar b){
            bar = b;
        }

        @Override
        public void run() {
            int progress = 0;
            long hack = System.currentTimeMillis();

            while(progress < 100) {
                progress = (int) ((System.currentTimeMillis() - hack) / 50);
                bar.setProgress(progress);
            }

        }
} 

Вот как это выглядит сейчас.

enter image description here

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Как упомянул @DavisHerring в комментарии, это (почти полностью) из-за пропущенных миллис в MyRunnable.run().Ваш код предполагает, что цикл будет «видеть» совершенно последовательные возвращаемые значения из System.currentTimeMillis().

Возможно, вы рассуждали, что поскольку цикл работает на полной скорости, он, несомненно, сможет вызвать System.currentTimeMillis() многораз каждую миллисекунду, и поэтому вряд ли что-либо будет пропущено.Но тогда у вас больше потоков, чем у ядер, поэтому очевидно, что некоторые потоки должны тратить часть своего времени на вытеснение.

Кроме того, в любой приоритетной многозадачной операционной системе нет никакой гарантии, что какой-то конкретный поток будетвыполняется в любое конкретное время.

Таким образом, если данный поток прекращает выполнение на короткое время, он может легко пропустить кратное 50. Например, один из потоков может «увидеть» следующие возвращаемые значенияиз System.currentTimeMillis():

1546215544594
1546215544594
1546215544594
1546215544594   <-- thread is unscheduled here, causing a 23ms gap
1546215544617
1546215544617
1546215544617
1546215544617

ПРЕДЛАГАЕМОЕ ИСПРАВЛЕНИЕ

Вместо того, чтобы пытаться поймать каждую милли, возьмите время взломать в начале.Затем просто сделайте progress равным (истекшее время) / 50:

progress = (int) ((System.currentTimeMillis() - hack) / 50);
bar.setProgress(progress);

В качестве отступления: System.currentTimeMillis() не гарантирует возврат монотонно увеличивающегося результата (см. System.uptimeTimeMillis() для альтернативы),но для вашей конкретной проблемы это, вероятно, не такая большая проблема.

0 голосов
/ 31 декабря 2018

Так как 1 поток занимает 5 секунд, и я сомневаюсь, что ваше устройство имеет 10 ядер (вероятно, 2 из 4, так как это, кажется, планшет), вы не получите все 10 для завершения в течение 5 секунд.Если вы делаете столько потоков / баров, сколько у вас ядер, это должно быть сделано примерно за 5 секунд.Если вы используете больше потоков, чем у вас ядер, потокам определенно придется делиться ядрами, чтобы выполнить работу, и это займет немного больше времени (но все же намного меньше, чем было бы синхронно).Даже при сопоставлении количества потоков с ядрами нет гарантии, что вы можете иметь бесперебойный доступ ко всем из них для своего приложения - все еще существует целая ОС и фоновые задачи, которые необходимо запустить.

Поскольку 1 поток занимает 5 секунд, тогда 10 синхронно займет 50 секунд.6-7 секунд на 10 потоков - это очень хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...