تانی یک کتابخانه Golang برای ایجاد و مدیریت استخر goroutine است، که به شما اجازه می دهد که با استفاده از رابط های همگام، کار از هر تعداد goroutine را محدود کنید و مدیریت کنید.

وقتی که کارهای شما از تعدادی منابع ناهمزمان می آید اما قابلیت پردازش موازی شما محدود است، یک استخر goroutine ثابت بسیار مفید است. به عنوان مثال، هنگام پردازش کارهای درخواست HTTP با مصرف CPU-منفی، می توانید یک استخر با اندازه تعداد پردازنده ها ایجاد کنید.

## نصب

```shell
go get github.com/Jeffail/tunny

به طور جایگزین، با استفاده از dep:

dep ensure -add github.com/Jeffail/tunny

استفاده

به اکثر موارد، کارهای داغ شما می تواند توسط یک func() ساده نشان داده شود، در این صورت می توانید از NewFunc استفاده کنید. بیایید ببینیم چگونه می توانیم از مثال ما از درخواست های HTTP به تعداد پردازنده ها استفاده کنیم:

package main

import (
	"io/ioutil"
	"net/http"
	"runtime"

	"github.com/Jeffail/tunny"
)

func main() {
	numCPUs := runtime.NumCPU()

	pool := tunny.NewFunc(numCPUs, func(payload interface{}) interface{} {
		var result []byte

		// TODO: انجام عملیات مصرف CPU-منفی با استفاده از داده ورودی

		return result
	})
	defer pool.Close()

	http.HandleFunc("/work", func(w http.ResponseWriter, r *http.Request) {
		input, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "خطای داخلی", http.StatusInternalServerError)
		}
		defer r.Body.Close()

		// وارد کردن این کار به استخر ما. این تماس همگام است و تا زمان اتمام کار بلاک می کند.
		result := pool.Process(input)

		w.Write(result.([]byte))
	})

	http.ListenAndServe(":8080", nil)
}

تانی همچنین از زمانبندی پشتیبانی می کند. می توانید فراخوانی Process فوق را با کد زیر جایگزین کنید:

result, err := pool.ProcessTimed(input, time.Second*5)
if err == tunny.ErrJobTimedOut {
	http.Error(w, "زمان درخواست تمام شد", http.StatusRequestTimeout)
}

همچنین می توانید از متن درخواست (یا هر متن دیگر) برای اداره زمانبندی و مهلت ها استفاده کنید. به سادگی فراخوانی Process را با کد زیر جایگزین کنید:

result, err := pool.ProcessCtx(r.Context(), input)
if err == context.DeadlineExceeded {
	http.Error(w, "زمان درخواست تمام شد", http.StatusRequestTimeout)
}

تغییر اندازه استخر

می توانید از SetSize(int) برای تغییر اندازه استخر تانی در هر زمان استفاده کنید.

pool.SetSize(10) // 10 goroutine
pool.SetSize(100) // 100 goroutine

این کار حتی اگر goroutine های دیگری همچنان در حال پردازش باشند، امن است.

Goroutine از دسترسی

گاهی اوقات، هر goroutine در استخر تانی به وضعیت مدیریت خود نیاز دارد. در این صورت باید tunny.Worker را پیاده سازی کنید، که شامل فراخوانی های پایان دادن، وقفه (اگر یک کار زمان بندی شده است و دیگر نیازی نیست) و مسدود کردن تخصیص کار بعدی تا زمانی که یک شرط خاص برآورده شود، است.

وقتی یک استخر با نوع Worker ایجاد می کنید، باید یک سازنده را برای تولید پیاده سازی سفارشی خودتان ارائه دهید:

pool := tunny.New(poolSize, func() Worker {
	// TODO: اختصاص وضعیت برای هر goroutine در اینجا
	return newCustomWorker()
})

به این ترتیب، تانی می تواند ساخت و از بین بردن نوع Worker را هنگام تغییر اندازه استخر انجام دهد.

ترتیب

از اینجا که پشتیبانی از ترتیب درجه حیاتی نیست. به دلیل پیاده سازی فعلی کانال ها و بلوک های انتخاب، پشته های کار برگردانده شده به عنوان یک صف FIFO پردازش خواهند شد. با این حال، این رفتار بخشی از مشخصات نیست و باید بر روی آن حساب نکنید.