هل هناك طريقة لاستخدام المكتبة الموازية للمهمة (TPL) مع SQLDATAREADER؟

StackOverflow https://stackoverflow.com/questions/3096727

سؤال

أنا أحب بساطة الموازية. للموازية. لطرق التمديد في TPL. كنت أتساءل عما إذا كانت هناك طريقة للاستفادة من شيء مشابه أو حتى مع المهام الأكثر تقدمًا قليلاً.

فيما يلي استخدام نموذجي لـ SQLDATAREADER ، وكنت أتساءل عما إذا كان ذلك ممكنًا ، وإذا كان الأمر كذلك في كيفية استبدال الحلقة أدناه بشيء في TPL. نظرًا لأن القارئ لا يمكن أن يوفر عددًا ثابتًا من التكرارات ، فإن طريقة التمديد غير ممكنة والتي تترك التعامل مع المهام التي سأجمعها. كنت آمل أن يكون شخص ما قد تعامل مع هذا بالفعل وعمل بعض ما يفعله و Don 'مع Ado.net.

using (SqlConnection conn = new SqlConnection("myConnString"))
using (SqlCommand comm = new SqlCommand("myQuery", conn))
{
    conn.Open();

    SqlDataReader reader = comm.ExecuteReader();

    if (reader.HasRows)
    {
        while (reader.Read())
        {
            // Do something with Reader
        }
    }
}
هل كانت مفيدة؟

المحلول

أنت هناك تقريبا. لف الرمز الذي نشرته في وظيفة مع هذا التوقيع:

IEnumerable<IDataRecord> MyQuery()

ثم استبدل // Do something with Reader رمز مع هذا:

yield return reader;

الآن لديك شيء يعمل في موضوع واحد. لسوء الحظ ، أثناء قراءة نتائج الاستعلام ، يتم إرجاع إشارة إلى نفس الكائن في كل مرة ، والكائن يتحول فقط لكل تكرار. هذا يعني أنه إذا حاولت تشغيله بالتوازي ، فستحصل على بعض النتائج الفردية حقًا لأن القراءات المتوازية تحفر الكائن المستخدم في مؤشرات الترابط المختلفة. تحتاج إلى رمز لأخذ ملف ينسخ من السجل لإرساله إلى حلقة متوازية.

في هذه المرحلة ، على الرغم من ذلك ، فإن ما أحب القيام به هو تخطي نسخة إضافية من السجل والانتقال مباشرة إلى فئة قوية. أكثر من ذلك ، أود استخدام طريقة عامة للقيام بذلك:

IEnumerable<T> GetData<T>(Func<IDataRecord, T> factory, string sql, Action<SqlParameterCollection> addParameters)
{
    using (var cn = new SqlConnection("My connection string"))
    using (var cmd = new SqlCommand(sql, cn))
    {
        addParameters(cmd.Parameters);

        cn.Open();
        using (var rdr = cmd.ExecuteReader())
        {
            while (rdr.Read())
            {
                yield return factory(rdr);
            }
        }
    }
}

على افتراض أن أساليب المصنع الخاصة بك ، قم بإنشاء نسخة كما هو متوقع ، يجب أن يكون هذا الرمز آمنًا لاستخدامه في حلقة متوازية. سيبدو استدعاء الطريقة مثل هذا (على افتراض فئة موظف مع طريقة مصنع ثابت تسمى "Create"):

var UnderPaid = GetData<Employee>(Employee.Create, 
       "SELECT * FROM Employee WHERE AnnualSalary <= @MinSalary", 
       p => {
           p.Add("@MinSalary", SqlDbType.Int).Value = 50000;
       });
Parallel.ForEach(UnderPaid, e => e.GiveRaise());

تحديث مهم:
أنا لست واثقًا من هذا الرمز كما كنت من قبل. لا يزال بإمكان مؤشر ترابط منفصل أن يطول القارئ بينما يكون مؤشر ترابط آخر في عملية صنع نسخته. يمكن أن أضع قفلًا حول ذلك ، لكنني قلق أيضًا من أن خيط آخر يمكنه استدعاء تحديث القارئ بعد أن تسمى النسخة الأصلية نفسها () ولكن قبل أن تبدأ في عمل النسخة. لذلك ، يتكون القسم الحرج هنا من الحلقة بأكملها ... وفي هذه المرحلة ، ستعود مرة أخرى. أتوقع أن تكون هناك طريقة لتعديل هذا الرمز للعمل كما هو متوقع للسيناريوهات متعددة الخيوط ، لكنها ستحتاج إلى مزيد من الدراسة.

نصائح أخرى

ستواجه صعوبة في استبدال ذلك أثناء الحلقة مباشرة. SQLDATAREADER ليست فئة آمنة مؤشر ترابط ، لذلك لا يمكنك استخدامها مباشرة من مؤشرات ترابط متعددة.

ومع ذلك ، يمكنك معالجة البيانات التي تقرأها باستخدام TPL. هناك بعض الخيارات ، هنا. قد يكون الأسهل في صنع خاص بك IEnumerable<T> التنفيذ الذي يعمل على القارئ ، ويعيد فئة أو بنية تحتوي على بياناتك. يمكنك بعد ذلك استخدام plinq أو Parallel.ForEach بيان لمعالجة بياناتك بالتوازي:

public IEnumerable<MyDataClass> ReadData()
{
    using (SqlConnection conn = new SqlConnection("myConnString"))
    using (SqlCommand comm = new SqlCommand("myQuery", conn))
    {
        conn.Open();

        SqlDataReader reader = comm.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                yield return new MyDataClass(... data from reader ...);
            }
        }
    }
}

بمجرد أن يكون لديك هذه الطريقة ، يمكنك معالجة هذا مباشرة ، عبر PlinQ أو TPL:

Parallel.ForEach(this.ReadData(), data =>
{
    // Use the data here...
});

أو:

this.ReadData().AsParallel().ForAll(data => 
{
    // Use the data here...
});
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top