مدل threading Binder برای تسهیل فراخوانی های تابع محلی طراحی شده است، حتی اگر این تماس ها ممکن است به یک فرآیند راه دور باشد. به طور خاص، هر فرآیندی که میزبان یک گره است باید دارای یک مجموعه از یک یا چند رشته بایندر باشد تا تراکنشهای گرههای میزبانی شده در آن فرآیند را انجام دهد.
تراکنش های همزمان و ناهمزمان
Binder از تراکنش های همزمان و ناهمزمان پشتیبانی می کند. بخش های زیر نحوه اجرای هر نوع تراکنش را توضیح می دهد.
تراکنش های همزمان
تراکنش های همزمان مسدود می شوند تا زمانی که در گره اجرا شوند و پاسخی برای آن تراکنش توسط تماس گیرنده دریافت شود. شکل زیر نحوه اجرای یک تراکنش همزمان را نشان می دهد:
شکل 1. تراکنش همزمان.
برای اجرای یک تراکنش همزمان، بایندر کارهای زیر را انجام می دهد:
- thread ها در threadpool هدف (T2 و T3) به درایور هسته تماس می گیرند تا منتظر کار ورودی باشند.
- هسته یک تراکنش جدید را دریافت می کند و یک نخ (T2) را در فرآیند هدف بیدار می کند تا تراکنش را مدیریت کند.
- رشته فراخوان (T1) مسدود می شود و منتظر پاسخ می ماند.
- فرآیند هدف تراکنش را اجرا می کند و یک پاسخ را برمی گرداند.
- رشته در فرآیند هدف (T2) به درایور هسته باز میگردد تا منتظر کار جدید بماند.
تراکنش های ناهمزمان
تراکنش های ناهمزمان برای تکمیل مسدود نمی شوند. به محض اینکه تراکنش به هسته منتقل شد، رشته فراخوان از حالت انسداد خارج می شود. شکل زیر نحوه اجرای تراکنش ناهمزمان را نشان می دهد:
شکل 2. تراکنش ناهمزمان.
- thread ها در threadpool هدف (T2 و T3) به درایور هسته تماس می گیرند تا منتظر کار ورودی باشند.
- هسته یک تراکنش جدید را دریافت می کند و یک نخ (T2) را در فرآیند هدف بیدار می کند تا تراکنش را مدیریت کند.
- رشته فراخوان (T1) به اجرا ادامه می دهد.
- فرآیند هدف تراکنش را اجرا می کند و یک پاسخ را برمی گرداند.
- رشته در فرآیند هدف (T2) به درایور هسته باز میگردد تا منتظر کار جدید بماند.
یک تابع همزمان یا ناهمزمان را شناسایی کنید
توابع علامت گذاری شده به عنوان oneway
در فایل AIDL ناهمزمان هستند. به عنوان مثال:
oneway void someCall();
اگر تابعی بهعنوان oneway
علامتگذاری نشده باشد، یک تابع همزمان است، حتی اگر تابع void
باشد.
سریال سازی تراکنش های ناهمزمان
Binder تراکنش های ناهمزمان را از هر گره منفرد سریال می کند. شکل زیر نشان می دهد که چگونه binder تراکنش های ناهمزمان را سریال می کند:
شکل 3. سریال سازی تراکنش های ناهمزمان.
- thread های موجود در threadpool هدف (B1 و B2) به درایور هسته فراخوانی می شود تا منتظر کار ورودی باشد.
- دو تراکنش (T1 و T2) روی یک گره (N1) به هسته ارسال می شود.
- هسته یک تراکنش جدید دریافت می کند و چون از یک گره (N1) هستند، آنها را سریالی می کند.
- تراکنش دیگری بر روی یک گره دیگر (N2) به هسته ارسال می شود.
- هسته سومین تراکنش را دریافت می کند و یک رشته (B2) را در فرآیند هدف بیدار می کند تا تراکنش را مدیریت کند.
- فرآیندهای هدف هر تراکنش را اجرا می کنند و یک پاسخ را برمی گرداند.
معاملات تو در تو
تراکنش های همزمان را می توان تودرتو کرد. رشته ای که یک تراکنش را مدیریت می کند می تواند یک تراکنش جدید صادر کند. تراکنش تودرتو می تواند به یک فرآیند متفاوت یا همان فرآیندی باشد که تراکنش فعلی را از آن دریافت کرده اید. این رفتار فراخوانی های تابع محلی را تقلید می کند. برای مثال، فرض کنید یک تابع با توابع تو در تو دارید:
def outer_function(x):
def inner_function(y):
def inner_inner_function(z):
اگر این ها تماس های محلی هستند، در همان رشته اجرا می شوند. به طور خاص، اگر فراخوان دهنده inner_function
نیز فرآیند میزبانی گره ای باشد که inner_inner_function
پیاده سازی می کند، فراخوانی inner_inner_function
در همان رشته اجرا می شود.
شکل زیر نشان می دهد که چگونه بایندر تراکنش های تودرتو را مدیریت می کند:
شکل 4. معاملات تو در تو.
- درخواست های رشته A1 در حال اجرا
foo()
. - به عنوان بخشی از این درخواست، رشته B1
bar()
را اجرا می کند که A روی همان رشته A1 اجرا می شود.
شکل زیر اجرای thread را در صورتی نشان می دهد که گره ای که bar()
را در فرآیند متفاوتی پیاده سازی می کند:
شکل 5. معاملات تودرتو در فرآیندهای مختلف.
- درخواست های رشته A1 در حال اجرا
foo()
. - به عنوان بخشی از این درخواست، رشته B1
bar()
را اجرا می کند که در رشته دیگر C1 اجرا می شود.
شکل زیر نشان می دهد که چگونه thread از همان فرآیند در هر نقطه از زنجیره تراکنش استفاده می کند:
شکل 6. تراکنش های تودرتو با استفاده مجدد از یک رشته.
- فرآیند A به فرآیند B فراخوانی می کند.
- فرآیند B به فرآیند C فراخوانی می شود.
- سپس فرآیند C مجدداً به فرآیند A فراخوانی میکند و هسته از رشته A1 در فرآیند A که بخشی از زنجیره تراکنش است، استفاده مجدد میکند.
برای تراکنش های ناهمزمان، تودرتو نقشی بازی نمی کند. مشتری منتظر نتیجه تراکنش ناهمزمان نمی ماند، بنابراین هیچ تودرتویی وجود ندارد. اگر کنترل کننده یک تراکنش ناهمزمان فرآیندی را که آن تراکنش ناهمزمان را صادر کرده است، فراخوانی کند، آن تراکنش را می توان در هر رشته آزاد در آن فرآیند مدیریت کرد.
از بن بست ها دوری کنید
تصویر زیر یک بن بست رایج را نشان می دهد:
شکل 7. بن بست مشترک.
- فرآیند A mutex MA را می گیرد و یک فراخوانی بایندر (T1) برای پردازش B ایجاد می کند که همچنین سعی می کند mutex MB را بگیرد.
- به طور همزمان، فرآیند B mutex MB را می گیرد و یک فراخوانی بایندر (T2) برای پردازش A ایجاد می کند که تلاش می کند mutex MA را بگیرد.
اگر این تراکنش ها با هم همپوشانی داشته باشند، هر تراکنش به طور بالقوه می تواند یک mutex در فرآیند خود بگیرد در حالی که منتظر است تا فرآیند دیگر یک mutex را آزاد کند و در نتیجه به بن بست منجر شود.
برای جلوگیری از بن بست در هنگام استفاده از کلاسور، هنگام برقراری تماس با کلاسور هیچ قفلی را نگه ندارید.
قوانین و بن بست های سفارش را قفل کنید
در یک محیط اجرایی واحد، اغلب با یک قانون ترتیب قفل از بن بست جلوگیری می شود. با این حال، هنگام برقراری تماس بین فرآیندها و بین پایگاههای کد، بهویژه زمانی که کد بهروزرسانی میشود، حفظ و هماهنگی یک قانون سفارش غیرممکن است.
تک موتکس و بن بست
با تراکنشهای تودرتو، فرآیند B میتواند مستقیماً به همان رشته در فرآیند A که یک mutex را نگه میدارد، فراخوانی کند. بنابراین، به دلیل بازگشت غیرمنتظره، هنوز هم می توان با یک mutex به بن بست رسید.
تماس های همزمان و بن بست
در حالی که تماسهای کلاسور ناهمزمان برای تکمیل مسدود نمیشوند، باید از نگهداشتن قفل برای تماسهای ناهمزمان نیز اجتناب کنید. اگر یک قفل نگه دارید، اگر یک تماس یک طرفه به طور تصادفی به تماس همزمان تغییر کند، ممکن است با مشکلات قفل مواجه شوید.
نخ کلاسور تک و بن بست
مدل تراکنش بایندر امکان ورود مجدد را فراهم میکند، بنابراین حتی اگر یک فرآیند دارای یک رشته بایندر باشد، همچنان به قفل کردن نیاز دارید. به عنوان مثال، فرض کنید در حال تکرار روی یک لیست در یک فرآیند تک رشته ای A هستید. برای هر آیتم در لیست، یک تراکنش بایندر خروجی انجام می دهید. اگر پیادهسازی تابعی که فراخوانی میکنید، یک تراکنش بایندر جدید به گرهای که در فرآیند A میزبانی میشود، انجام دهد، آن تراکنش در همان رشتهای انجام میشود که لیست را تکرار میکرد. اگر اجرای آن تراکنش همان لیست را اصلاح کند، ممکن است در زمانی که بعداً به تکرار روی لیست ادامه دهید، با مشکلاتی مواجه شوید.
اندازه threadpool را پیکربندی کنید
هنگامی که یک سرویس چندین مشتری دارد، افزودن رشته های بیشتر به Threadpool می تواند اختلاف را کاهش دهد و تماس های بیشتری را به صورت موازی ارائه دهد. بعد از اینکه به درستی با همزمانی برخورد کردید، می توانید موضوعات بیشتری اضافه کنید. مشکلی که میتواند با اضافه کردن رشتههای بیشتر ایجاد شود که ممکن است برخی از رشتهها در طول بارهای کاری بیصدا استفاده نشوند.
Thread ها بر حسب تقاضا ایجاد می شوند تا زمانی که یک حداکثر پیکربندی شده باشد.
کتابخانه libbinder به طور پیش فرض 15 رشته دارد. برای تغییر این مقدار setThreadPoolMaxThreadCount
استفاده کنید:
using ::android::ProcessState;
ProcessState::self()->setThreadPoolMaxThreadCount(size_t maxThreads);