1 Востаннє редагувалося sensei (04.12.2016 02:00:33)

Тема: Аргументи у потоці

Всім привіт!
Зіштовхнувся з проблемкою в наступному коді при передачі аргументів у потік

int start = part * i;
int end = part * (i + 1);
threadsArr[i] = new Thread(() => ThreadSearch(arr, searchedVal, start, end));

у вас є ідеї, чому якщо замінити аргументи безпосередньо на rvalue

threadsArr[i] = new Thread(() => ThreadSearch(arr, searchedVal, part * i, part * (i + 1)));

, то в потік передається не коректна інформація (результат множення виходить такий, ніби "i" більше за позволене у for'i)

2

Re: Аргументи у потоці

А можете дати не свої висновки, а первинну інформацію (який саме результат множення виходить)?
І решту коду теж було б непогано - як проголошені i та part, як використовуються поза цим фрагментом?
І що ви взагалі хочете зробити

Якщо я правильно зрозумів, що у вас i стає рівним верхній межі, то ви не передаєте в тред значення i, а захоплюєте змінну за посиланням, відповідно, береться її поточне значення. start і end же кожного разу проголошуються нові.

Подякували: sensei1

3

Re: Аргументи у потоці

koala написав:

А можете дати не свої висновки, а первинну інформацію (який саме результат множення виходить)?
І решту коду теж було б непогано - як проголошені i та part, як використовуються поза цим фрагментом?
І що ви взагалі хочете зробити

Задача - розпаралелити лінійний пошук
Проблема вилазить, коли значення у потік я передаю не змінними, а безпосереднім результатом множенням, тоді крешає у функції ThreadSearch, бо туди прийшли старт = 25 і енд = 30, а таке могло статись, тільки якщо 
i == threadsCount

static void ParalellSearch(int[] arr, int searchedVal)
        {
            int threadsCount = 5;
            Thread[] threadsArr = new Thread[threadsCount];
            int part = arr.Length / threadsCount; // arr.Length == 25
            for (int i = 0; i < threadsCount; i++)
            {
                int start = part * i;
                int end = part * (i + 1);
                threadsArr[i] = new Thread(() => ThreadSearch(arr, searchedVal, start, end));
                threadsArr[i].Start();
            }
            for (int i = 0; i < threadsCount - 1; i++) 
            {
                threadsArr[i].Join();
            }
        }

static void ThreadSearch(int[] arr, int searchedVal, int start, int end)
        {
            for (int i = start; i < end; i++)
            {
                if (arr[i] == searchedVal)
                {
                    foundIndexes.Add(i);
                }
            }
        }

4

Re: Аргументи у потоці

Як я і казав: змінні зв'язуються за посиланням, і значення змінної i береться лямбдою вже по закінченні циклу.

Подякували: sensei1

5

Re: Аргументи у потоці

дякую, але мені здаєтья, що те що значення береться по посиланню в даному випадку ніяк не впливає, оскільки ми передаємо результат множення, а не саме i(та і саме i ніяк не модифікується ніде). Думаю проблемою було це

koala написав:

значення змінної i береться лямбдою вже по закінченні циклу.

, але мені не дуже вкладаєтсья в голову чому це так працює

6

Re: Аргументи у потоці

Ну давайте розбирати по кроках.

threadsArr[i] = new Thread(() => ThreadSearch(arr, searchedVal, start, end));

Спершу проголошується лямбда

() => ThreadSearch(arr, searchedVal, start, end)

Фактично це структура з функції і посилань на змінні.
Далі іде виклик new Thread(..) - при цьому потік ще не запускається (це потребує багато часу), але запам'ятовує, яка саме лямбда йому передана.
І нарешті в потоці викликається лямбда, що запитує значення зв'язаної змінної i, яке на цей момент вже не має сенсу.
Давайте поексперементуємо:

            int start;
            int end;
            for (int i = 0; i < threadsCount; i++)
            {
                start = part * i;
                end = part * (i + 1);
                threadsArr[i] = new Thread(() => ThreadSearch(arr, searchedVal, start, end));
                threadsArr[i].Start();
            }

Змінні не ініціалізуються, а присвоюються - а отже, результат має бути так само неправильний, бо всі лямбди посилатимуться на одні й ті самі змінні.

Подякували: sensei, Engineer2

7

Re: Аргументи у потоці

{
    threadsArr[i] = new Thread(() => ThreadSearch(arr, searchedVal, part * i, part * (i + 1)));
    threadsArr[i].Start();
}

ага, тобто обчислення part * i відбудеться коли сред буде запускатися, а оскільки це процес довгий, то ітерація встигне закінчитись і відповідно і зміниться, так?

8

Re: Аргументи у потоці

Саме так. Лямбда буде виконана вже після виклику Thread, і, відповідно, пізно.

Подякували: sensei1

9

Re: Аргументи у потоці

файно, дякую :)