Eşzamanlılık çalışma zamanında genel en iyi yöntemler
Bu belge, eşzamanlılık zamanının birden fazla alana uygulamak için en iyi yöntemler açıklanır.
Bölümler
Bu belge aşağıdaki bölümleri içerir:
İşbirliği yapan eşitleme yapıları mümkün olduğunda kullanın.
Verim değil uzun görevlerden kaçının
Engellemek veya gecikme süresi yüksek olan işlemleri kaydırmak için Oversubscription kullanın.
Mümkün olduğunda aynı anda bellek yönetimi işlevlerini kullanın
Yaşam eşzamanlılık nesneleri yönetmek için RAII kullanın.
Genel kapsamda eşzamanlılık nesneleri oluşturma
Eşzamanlılık nesneler paylaşılan veri kesimlerini kullanmayın.
İşbirliği yapan eşitleme yapıları mümkün olduğunda kullanın.
Eşzamanlılık zamanı dış eşitleme nesnesi gerektirmeyen birçok eşzamanlılık güvenli yapılar sağlar. Örneğin, concurrency::concurrent_vector SAX eşzamanlılık güvenli ekleme ve öğe Operasyon'a erişmek. Ancak, özel bir kaynağa erişim izni gerekli olduğu durumlarda, çalışma zamanı sağlar concurrency::critical_section, concurrency::reader_writer_lock, ve concurrency::event sınıfları. Bu tür cooperatively davranır; Bu nedenle ilk görev verilerini bekler gibi Görev Zamanlayıcı işleme başka bir içerik kaynakları yeniden tahsis edebilirsiniz. Mümkün olduğunda, bu eşitleme türleri cooperatively davranır değil, diğer eşitleme mekanizmaları yerine, Windows API'si tarafından sağlanan gibi kullanın. Bu eşitleme türleri ve kod örneği hakkında daha fazla bilgi için bkz: Eşitleme veri yapıları ve Windows API için eşitleme veri yapılarını karşılaştırma.
Top
Verim değil uzun görevlerden kaçının
Görev Zamanlayıcı'yı cooperatively davranır, çünkü görevleri arasında eşitlik sağlamaz. Bu nedenle, bir görev diğer görevler başlatılmasını engelleyebilir. Bazı durumlarda kabul edilebilir olmasına karşın, diğer durumlarda bu kilitlenme ya da sorunlar neden olabilir.
Aşağıdaki örnek, tahsis edilen işlem kaynakları sayısından daha fazla görevleri gerçekleştirir. İlk görev, Görev Zamanlayıcı'yı verim değil ve ilk görev bitene kadar bu nedenle ikinci görev başlatılamıyor.
// cooperative-tasks.cpp
// compile with: /EHsc
#include <ppl.h>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
// Data that the application passes to lightweight tasks.
struct task_data_t
{
int id; // a unique task identifier.
event e; // signals that the task has finished.
};
// A lightweight task that performs a lengthy operation.
void task(void* data)
{
task_data_t* task_data = reinterpret_cast<task_data_t*>(data);
// Create a large loop that occasionally prints a value to the console.
int i;
for (i = 0; i < 1000000000; ++i)
{
if (i > 0 && (i % 250000000) == 0)
{
wstringstream ss;
ss << task_data->id << L": " << i << endl;
wcout << ss.str();
}
}
wstringstream ss;
ss << task_data->id << L": " << i << endl;
wcout << ss.str();
// Signal to the caller that the thread is finished.
task_data->e.set();
}
int wmain()
{
// For illustration, limit the number of concurrent
// tasks to one.
Scheduler::SetDefaultSchedulerPolicy(SchedulerPolicy(2,
MinConcurrency, 1, MaxConcurrency, 1));
// Schedule two tasks.
task_data_t t1;
t1.id = 0;
CurrentScheduler::ScheduleTask(task, &t1);
task_data_t t2;
t2.id = 1;
CurrentScheduler::ScheduleTask(task, &t2);
// Wait for the tasks to finish.
t1.e.wait();
t2.e.wait();
}
Bu örnek aşağıdaki çıktıyı üretir:
1: 250000000
1: 500000000
1: 750000000
1: 1000000000
2: 250000000
2: 500000000
2: 750000000
2: 1000000000
İki görev arasında işbirliği sağlamak için çeşitli yolları vardır. Görev Zamanlayıcısı'nda uzun süre çalışan bir görev için bazen verim için yollarından biridir. Aşağıdaki örnek değiştirir task işlevini çağırmak için concurrency::Context::Yield başka bir görev çalıştırabilmeniz için Görev Zamanlayıcı'yı yürütme ödeme yöntemi.
// A lightweight task that performs a lengthy operation.
void task(void* data)
{
task_data_t* task_data = reinterpret_cast<task_data_t*>(data);
// Create a large loop that occasionally prints a value to the console.
int i;
for (i = 0; i < 1000000000; ++i)
{
if (i > 0 && (i % 250000000) == 0)
{
wstringstream ss;
ss << task_data->id << L": " << i << endl;
wcout << ss.str();
// Yield control back to the task scheduler.
Context::Yield();
}
}
wstringstream ss;
ss << task_data->id << L": " << i << endl;
wcout << ss.str();
// Signal to the caller that the thread is finished.
task_data->e.set();
}
Bu örnek aşağıdaki çıktıyı üretir:
1: 250000000
2: 250000000
1: 500000000
2: 500000000
1: 750000000
2: 750000000
1: 1000000000
2: 1000000000
Context::Yield Yöntemi, yalnızca başka bir etkin parçacığında geçerli iş parçacığının ait olduğu basit bir görev veya başka bir işletim sistemi iş parçacığı Zamanlayıcı üretir. Bu yöntemin çalışması için çalışmak üzere zamanlandığı verim değil bir concurrency::task_group veya concurrency::structured_task_group nesne ancak henüz başlatılmadı.
Uzun süredir çalışan görevleri arasında işbirliği sağlamak için başka yolları da vardır. Büyük bir görev daha küçük alt göreve kesebilirsiniz. Oversubscription uzun bir görev sırasında da etkinleştirebilirsiniz. Oversubscription, kullanılabilir donanım iş parçacığı sayısından daha fazla iş parçacığı oluşturmanızı sağlar. Yüksek miktarda disk ya da bir ağ bağlantısı veri okuma gecikmesi, örneğin, uzun görev içeren oversubscription özellikle yararlıdır. Basit görevleri ve oversubscription hakkında daha fazla bilgi için bkz: Görev Zamanlayıcı'yı (eşzamanlılık çalışma zamanı).
Top
Engellemek veya gecikme süresi yüksek olan işlemleri kaydırmak için Oversubscription kullanın.
Eşzamanlılık zamanı eşitleme ilkel gibi sağlar concurrency::critical_section, cooperatively engellemek ve birbirlerine verim görevleri etkinleştirin. Bir görev cooperatively engeller veya üretir, Görev Zamanlayıcı'yı bir ilk görev verilerini bekler gibi işleme başka bir içerik kaynakları yeniden tahsis edebilirsiniz.
Eşzamanlılık çalışma zamanı tarafından sağlanan işbirliği yapan engelleme mekanizması kullanamadığı durumlarda vardır. Örneğin, kullandığınız bir harici kitaplık farklı eşitleme mekanizması kullanabilirsiniz. Windows API kullandığınızda, gecikme süresi, yüksek miktarda örnek olabilir bir işlemi gerçekleştirdiğinizde bir örnek ise, ReadFile işlevi ağ bağlantısından verileri okunamıyor. Bu gibi durumlarda, başka bir görev boşta kaldığında çalışacak diğer görevleri oversubscription etkinleştirebilirsiniz. Oversubscription, kullanılabilir donanım iş parçacığı sayısından daha fazla iş parçacığı oluşturmanızı sağlar.
Aşağıdaki işlevi göz önünde download, belirtilen URL'deki dosya yükler. Bu örnek concurrency::Context::Oversubscribe geçici olarak etkin iş parçacıkları sayısını artırmak için yöntem.
// Downloads the file at the given URL.
string download(const string& url)
{
// Enable oversubscription.
Context::Oversubscribe(true);
// Download the file.
string content = GetHttpFile(_session, url.c_str());
// Disable oversubscription.
Context::Oversubscribe(false);
return content;
}
Çünkü GetHttpFile işlevi potansiyel görünmeyen bir işlem gerçekleştirir, oversubscription, veriler için geçerli görev beklediği gibi çalıştırmak diğer görevleri etkinleştirebilir. Bu örnek tam sürümü için bkz: Nasıl yapılır: gecikme kaydırmak için Oversubscription kullanın.
Top
Mümkün olduğunda aynı anda bellek yönetimi işlevlerini kullanın
Bellek yönetimi işlevlerini concurrency::Alloc ve concurrency::Free, oldukça kısa bir yaşam süresine sahip küçük nesneleri sık tahsis belirtilecek görevleri vardır. Eşzamanlılık çalýþma zamaný her çalışan iş parçacığı için ayrı bir önbellek tutar. Alloc Ve Free işlevlerini ayırmak ve bu önbelleklere kilitleri ya da bellek engelleri kullanmadan bellekten serbest.
Bu bellek yönetimi işlevleri hakkında daha fazla bilgi için bkz: Görev Zamanlayıcı'yı (eşzamanlılık çalışma zamanı). Bu işlevleri kullanan bir örnek için bkz: Nasıl yapılır: ayırma kullanın ve boş bellek performansını artırmak.
Top
Yaşam eşzamanlılık nesneleri yönetmek için RAII kullanın.
Eşzamanlılık çalışma zamanı özel durum işleme iptali gibi özellikleri uygulamak için kullanır. Bu nedenle, zamanına arayın ya da zamanına çağıran başka bir kütüphane arama özel durum-güvenli kod yazmak.
Olan kaynak alma başlatma (RAII) deseni olan güvenle eşzamanlılık nesne belirli bir kapsam altında yaşam yönetmek için bir yöntem. RAII düzeni altında yığında bir veri yapısı tahsis edilir. Bu veri yapısı başlatır veya oluşturulur ve bozar veya veri yapısı bozulduğunda bu kaynağı bırakır, kaynak isteklerdir. RAII desen kapsayan kapsam kapanması yıkıcı adlı garanti eder. Birden fazla fonksiyon içeren bu desen yararlıdır return deyimleri. Bu desen, özel durum-güvenli kod yazma da yardımcı olur. Zaman bir throw deyimi neden olan yığın açılımı yıkıcı RAII nesnesi olarak adlandırılan için; Bu nedenle, kaynak her zaman doğru serbest bırakılmış veya silinmiş.
Örneğin, RAII desen kullanan birçok sınıf çalışma zamanı tanımlar concurrency::critical_section::scoped_lock ve concurrency::reader_writer_lock::scoped_lock. Bu yardımcı sınıf olarak bilinen kilitleri kapsamlı. Çalışırken bu sınıflar birçok avantaj sunar concurrency::critical_section veya concurrency::reader_writer_lock nesneler. Bu sınıfların kurucu erişim sağlanan edinme critical_section veya reader_writer_lock nesne; Bu nesneye erişimi yıkıcı yayımlar. Edildiğinde, kapsamlı bir kilit, karşılıklı dışlama nesneye erişimi otomatik olarak yayımlar için el ile alttaki nesne kilidini değildir.
Aşağıdaki sınıf göz önünde account, bir harici kitaplık tarafından tanımlanır ve bu nedenle değiştirilemez.
// account.h
#pragma once
#include <exception>
#include <sstream>
// Represents a bank account.
class account
{
public:
explicit account(int initial_balance = 0)
: _balance(initial_balance)
{
}
// Retrieves the current balance.
int balance() const
{
return _balance;
}
// Deposits the specified amount into the account.
int deposit(int amount)
{
_balance += amount;
return _balance;
}
// Withdraws the specified amount from the account.
int withdraw(int amount)
{
if (_balance < 0)
{
std::stringstream ss;
ss << "negative balance: " << _balance << std::endl;
throw std::exception((ss.str().c_str()));
}
_balance -= amount;
return _balance;
}
private:
// The current balance.
int _balance;
};
Birden fazla hareketlerini gerçekleştirir aşağıdaki örnek bir account paralel nesnesi. Örnek bir critical_section nesne erişimi eşitlemek için account nesnesini account sınıf eşzamanlılık güvenli değil. Her paralel işlem kullanan bir critical_section::scoped_lock , güvence altına almak için nesne critical_section nesnedir kilidi işlemi başarılı veya başarısız. Hesap bakiyesi negatif olduğunda withdraw işlemi başarısız bir özel durum üretiliyor tarafından.
// account-transactions.cpp
// compile with: /EHsc
#include "account.h"
#include <ppl.h>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create an account that has an initial balance of 1924.
account acc(1924);
// Synchronizes access to the account object because the account class is
// not concurrency-safe.
critical_section cs;
// Perform multiple transactions on the account in parallel.
try
{
parallel_invoke(
[&acc, &cs] {
critical_section::scoped_lock lock(cs);
wcout << L"Balance before deposit: " << acc.balance() << endl;
acc.deposit(1000);
wcout << L"Balance after deposit: " << acc.balance() << endl;
},
[&acc, &cs] {
critical_section::scoped_lock lock(cs);
wcout << L"Balance before withdrawal: " << acc.balance() << endl;
acc.withdraw(50);
wcout << L"Balance after withdrawal: " << acc.balance() << endl;
},
[&acc, &cs] {
critical_section::scoped_lock lock(cs);
wcout << L"Balance before withdrawal: " << acc.balance() << endl;
acc.withdraw(3000);
wcout << L"Balance after withdrawal: " << acc.balance() << endl;
}
);
}
catch (const exception& e)
{
wcout << L"Error details:" << endl << L"\t" << e.what() << endl;
}
}
Bu örnek, aşağıdaki örnek çıktı üretir:
Balance before deposit: 1924
Balance after deposit: 2924
Balance before withdrawal: 2924
Balance after withdrawal: -76
Balance before withdrawal: -76
Error details:
negative balance: -76
RAII desen yaşam eşzamanlılık nesneleri yönetmek için kullandığınız ek örnekler için bkz: İzlenecek yol: Kullanıcı arabirimi akıştan çalışma kaldırma, Nasıl yapılır: işbirlikçi bir semafor uygulamak için içerik sınıfını kullanın, ve Nasıl yapılır: gecikme kaydırmak için Oversubscription kullanın.
Top
Genel kapsamda eşzamanlılık nesneleri oluşturma
Genel kapsamda eşzamanlılık nesnesi oluşturduğunuzda, uygulamanızda gerçekleşmesi erişim ihlalleri kilitlenme veya bellek gibi sorunlara neden olabilir.
Eşzamanlılık çalışma zamanı nesnesi oluşturduğunuzda, biri henüz oluşturulmamış, çalışma zamanı, varsayılan Zamanlayıcı oluşturur. Genel nesne oluşturma sırasında oluşturulan bir çalışma zamanı buna göre bu varsayılan Zamanlayıcı oluşturmak çalışma zamanı neden olur. Ancak, bu işlem diğer nesnelerin eşzamanlılık çalışma zamanı altyapısı desteği başlatma etkileyebilir dahili bir kilit alır. Bu iç kilit henüz başlatılmamış ve böylece uygulamada kilitlenme oluşmasına neden başka bir altyapı nesne gerekli olabilir.
Aşağıdaki örnekte global oluşturulması gösterilmiştir concurrency::Scheduler nesnesi. Bu desen için değil yalnızca geçerlidir Scheduler sınıfı ancak eşzamanlılık çalışma zamanı tarafından sağlanan tüm türleri. Uygulamanızda beklenmeyen davranışlara neden çünkü bu desen gitmezler öneririz.
// global-scheduler.cpp
// compile with: /EHsc
#include <concrt.h>
using namespace concurrency;
static_assert(false, "This example illustrates a non-recommended practice.");
// Create a Scheduler object at global scope.
// BUG: This practice is not recommended because it can cause deadlock.
Scheduler* globalScheduler = Scheduler::Create(SchedulerPolicy(2,
MinConcurrency, 2, MaxConcurrency, 4));
int wmain()
{
}
Doğru şekilde oluşturma örnekleri için Scheduler nesneleri Bkz: Görev Zamanlayıcı'yı (eşzamanlılık çalışma zamanı).
Top
Eşzamanlılık nesneler paylaşılan veri kesimlerini kullanmayın.
Eşzamanlılık çalışma zamanı paylaşılan veri bölümünde, örneğin tarafından oluşturulan bir veri bölümü eşzamanlılık nesnelerinin kullanımını desteklemez data_seg#pragma yönergesi. İşlem sınırları boyunca paylaşılan bir eşzamanlılık nesne tutarsız veya geçersiz bir durumda çalışma zamanı koyabilirsiniz.
Top
Ayrıca bkz.
Görevler
Nasıl yapılır: ayırma kullanın ve boş bellek performansını artırmak
Nasıl yapılır: gecikme kaydırmak için Oversubscription kullanın
Nasıl yapılır: işbirlikçi bir semafor uygulamak için içerik sınıfını kullanın
İzlenecek yol: Kullanıcı arabirimi akıştan çalışma kaldırma
Kavramlar
Paralel Desenler kitaplığının (ppl)
Zaman uyumsuz aracıları kitaplığı
Görev Zamanlayıcı'yı (eşzamanlılık çalışma zamanı)
Windows API için eşitleme veri yapılarını karşılaştırma
Paralel desen Kitaplığı'nda en iyi yöntemler
Zaman uyumsuz aracıları Kitaplığı'nda en iyi yöntemler