Питхон Тхреадинг: Увод – вдзвдз

У овом водичу ћете научити како да користите Питхон-ов уграђени модул за обраду нити да истражите могућности вишенитног рада у Питхон-у.

Почевши од основа процеса и нити, научићете како вишенитност функционише у Питхон-у — док разумете концепте конкурентности и паралелизма. Затим ћете научити како да покренете и покренете једну или више нити у Питхон-у користећи уграђени модул за обраду нити.

Хајде да почнемо.

Процеси наспрам нити: које су разлике?

Шта је процес?

Процес је свака инстанца програма која треба да се покрене.

То може бити било шта – Питхон скрипта или веб прегледач као што је Цхроме до апликације за видео конференције. Ако покренете Таск Манагер на вашој машини и одете до Перформансе –> ЦПУ, моћи ћете да видите процесе и нити које се тренутно покрећу на вашим језграма ЦПУ-а.

Разумевање процеса и нити

Интерно, процес има наменску меморију која чува код и податке који одговарају процесу.

Процес се састоји од једне или више нити. Нит је најмањи низ инструкција које оперативни систем може да изврши и представља ток извршења.

Свака нит има свој стек и регистре, али не и наменску меморију. Све нити повезане са процесом могу приступити подацима. Стога, податке и меморију деле све нити процеса.

У ЦПУ-у са Н језгара, Н процеса може да се извршава паралелно у истом тренутку. Међутим, две нити истог процеса никада не могу да се извршавају паралелно – али могу да се извршавају истовремено. У следећем одељку ћемо се позабавити концептом конкурентности наспрам паралелизма.

На основу онога што смо до сада научили, хајде да сумирамо разлике између процеса и нити.

ФеатуреПроцессТхреадМемориДедицатед мемориСхаред мемориМоде оф екецутионПараллел, цонцуррентЦонцуррент; али не и параллелЕкецутион којим управља Оперативни системЦПитхон Интерпретер

Мултитхреадинг у Питхон-у

У Питхон-у, Глобално закључавање тумача (ГИЛ) осигурава да само једна нит може стећи закључавање и покренути у било ком тренутку. Све нити треба да стекну ово закључавање да би се покренуле. Ово осигурава да само једна нит може да буде у извршавању — у било ком тренутку — и избегава истовремено вишенитност.

На пример, размотрите две нити, т1 и т2, истог процеса. Пошто нити деле исте податке када т1 чита одређену вредност к, т2 може да модификује исту вредност к. Ово може довести до застоја и нежељених резултата. Али само једна од нити може добити закључавање и покренути у било ком случају. Стога, ГИЛ такође обезбеђује сигурност нити.

Дакле, како да постигнемо вишенитне могућности у Питхон-у? Да бисмо ово разумели, хајде да разговарамо о концептима конкурентности и паралелизма.

Конкуренција против паралелизма: Преглед

Размотрите ЦПУ са више од једног језгра. На слици испод, ЦПУ има четири језгра. То значи да у сваком тренутку можемо имати четири различите операције које раде паралелно.

Ако постоје четири процеса, онда сваки од процеса може да се покреће независно и истовремено на сваком од четири језгра. Претпоставимо да сваки процес има две нити.

Да бисмо разумели како функционише нитирање, пређимо са вишејезгрене на једнојезгарну процесорску архитектуру. Као што је поменуто, само једна нит може бити активна у одређеној инстанци извршења; али језгро процесора може да прелази између нити.

На пример, И/О везане нити често чекају на И/О операције: читање корисничког уноса, читање базе података и операције са датотекама. Током овог времена чекања, може да отпусти закључавање да би друга нит могла да се покрене. Време чекања такође може бити једноставна операција као што је спавање у трајању од н секунди.

Укратко: током операција чекања, нит отпушта закључавање, омогућавајући језгру процесора да се пребаци на другу нит. Ранија нит наставља са извршавањем након завршетка периода чекања. Овај процес, где се језгро процесора истовремено пребацује између нити, олакшава вишенитност. ✅

Ако желите да имплементирате паралелизам на нивоу процеса у вашој апликацији, размислите о коришћењу вишепроцесирања.

Питхон Тхреадинг модул: Први кораци

Питхон се испоручује са модулом за обраду нити који можете да увезете у Питхон скрипту.

import threading

Да бисте креирали објекат нити у Питхон-у, можете користити конструктор нити: тхреадинг.Тхреад(…). Ово је генеричка синтакса која је довољна за већину имплементација нити:

threading.Thread(target=...,args=...)

овде,

  • таргет је аргумент кључне речи који означава Питхон који се може позвати
  • аргс је скуп аргумената које циљ узима.

Биће вам потребан Питхон 3.к да бисте покренули примере кода у овом водичу. Преузмите код и пратите га.

Како дефинисати и покренути нити у Питхон-у

Хајде да дефинишемо нит која покреће циљну функцију.

Циљна функција је соме_фунц.

import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
print(threading.active_count())

Хајде да анализирамо шта ради горњи исечак кода:

  • Увози модуле навоја и времена.
  • Функција соме_фунц има дескриптивне изјаве принт() и укључује операцију спавања у трајању од две секунде: тиме.слееп(н) узрокује да функција спава н секунди.
  • Затим дефинишемо нит тхреад_1 са циљем као соме_фунц. тхреадинг.Тхреад(таргет=…) креира објекат нити.
  • Напомена: Наведите име функције, а не позив функције; користите соме_фунц а не соме_фунц().
  • Креирање објекта нити не покреће нит; позивање методе старт() на објекту нити.
  • Да бисмо добили број активних нити, користимо функцију ацтиве_цоунт().

Питхон скрипта ради на главној нити, а ми креирамо другу нит (тхреад1) за покретање функције соме_фунц тако да је број активних нити два, као што се види у излазу:

# Output
Running some_func...
2
Finished running some_func.

Ако боље погледамо излаз, видећемо да се након покретања тхреад1 покреће прва изјава за штампање. Али током операције мировања, процесор се пребацује на главну нит и штампа број активних нити — без чекања да се тхреад1 заврши са извршавањем.

Чека се да се нити заврше са извршавањем

Ако желите да тхреад1 заврши извршење, можете позвати метод јоин() на њему након покретања нити. То ће сачекати да тхреад1 заврши извршење без преласка на главну нит.

import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
thread1.join()
print(threading.active_count())

Сада је тхреад1 завршио са извршавањем пре него што одштампамо активни број нити. Дакле, само главна нит је покренута, што значи да је број активних нити један. ✅

# Output
Running some_func...
Finished running some_func.
1

Како покренути више нити у Питхон-у

Затим, направимо две нити за покретање две различите функције.

Овде је цоунт_довн функција која узима број као аргумент и одбројава од тог броја до нуле.

def count_down(n):
    for i in range(n,-1,-1):
        print(i)

Дефинишемо цоунт_уп, још једну Питхон функцију која броји од нуле до датог броја.

def count_up(n):
    for i in range(n+1):
        print(i)

📑 Када користите функцију ранге() са опсегом синтаксе (старт, стоп, степ), крајња тачка заустављања је подразумевано искључена.

– За одбројавање од одређеног броја до нуле, можете користити негативну вредност корака од -1 и поставити вредност заустављања на -1 тако да је нула укључена.

– Слично томе, да бисте бројали до н, морате да поставите вредност заустављања на н + 1. Пошто су подразумеване вредности за почетак и корак 0 и 1, респективно, можете користити опсег (н + 1) да бисте добили низ 0 кроз н.

Затим дефинишемо две нити, тхреад1 и тхреад2 за покретање функција цоунт_довн и цоунт_уп, респективно. Додамо изјаве за штампање и операције спавања за обе функције.

Када креирате објекте нити, обратите пажњу да аргументе циљне функције треба навести као тупле—параметар аргс. Пошто обе функције (цоунт_довн и цоунт_уп) узимају један аргумент. Мораћете да уметнете зарез експлицитно после вредности. Ово осигурава да се аргумент и даље прослеђује као тупле, јер се наредни елементи закључују као Ништа.

import threading
import time

def count_down(n):
    for i in range(n,-1,-1):
        print("Running thread1....")
        print(i)
        time.sleep(1)


def count_up(n):
    for i in range(n+1):
        print("Running thread2...")
        print(i)
        time.sleep(1)

thread1 = threading.Thread(target=count_down,args=(10,))
thread2 = threading.Thread(target=count_up,args=(5,))
thread1.start()
thread2.start()

У излазу:

  • Функција цоунт_уп ради на тхреад2 и броји до 5 почевши од 0.
  • Функција цоунт_довн ради на тхреад1 одбројава од 10 до 0.
# Output
Running thread1....
10
Running thread2...
0
Running thread1....
9
Running thread2...
1
Running thread1....
8
Running thread2...
2
Running thread1....
7
Running thread2...
3
Running thread1....
6
Running thread2...
4
Running thread1....
5
Running thread2...
5
Running thread1....
4
Running thread1....
3
Running thread1....
2
Running thread1....
1
Running thread1....
0

Можете видети да се тхреад1 и тхреад2 извршавају алтернативно, јер обе укључују операцију чекања (спавање). Када функција цоунт_уп заврши са бројањем до 5, тхреад2 више није активан. Тако добијамо излаз који одговара само тхреад1.

Сумирајући

У овом водичу сте научили како да користите Питхон-ов уграђени модул за обраду нити за имплементацију вишенитности. Ево резимеа кључних закључака:

  • Конструктор нити се може користити за креирање објекта нити. Коришћење тхреадинг.Тхреад(таргет=<цаллабле>,аргс=(<тортуре оф аргс>)) креира нит која покреће циљни позив са аргументима наведеним у аргс.
  • Питхон програм ради на главној нити, тако да су објекти нити које креирате додатне нити. Можете позвати функцију ацтиве_цоунт() која враћа број активних нити у било којој инстанци.
  • Можете покренути нит помоћу методе старт() на објекту нити и сачекати док се не заврши извршавање помоћу методе јоин().

Можете да кодирате додатне примере подешавањем времена чекања, покушајем друге И/О операције и још много тога. Обавезно имплементирајте вишенитност у своје предстојеће Питхон пројекте. Срећно кодирање!🎉