2010-02-24 66 views
5

Tôi đang làm việc trên một ứng dụng iPhone sử dụng cơ sở dữ liệu sqlite. Ứng dụng tải xuống dữ liệu ngoài Internet trong chuỗi nền với giao diện người dùng trong chuỗi chính. Chủ đề tải xuống nền có thể tạo sẵn INSERT, UPDATE và SELECT trên cơ sở dữ liệu. Lớp UI cũng có thể tương tác với cơ sở dữ liệu bằng cách thực hiện UPDATE và SELECT. Nếu tôi không tương tác nhiều với giao diện người dùng trong khi chuỗi nền đang tải xuống, mọi thứ đều hoạt động tốt. Tuy nhiên, tôi bắt đầu gặp sự cố khi nhiều CẬP NHẬT trên luồng chính (UI) được thực hiện trong khi quá trình tải xuống đang diễn ra.Các vấn đề về tương tranh sqlite

Ứng dụng luôn thoát khi đang cố chạy chức năng cơ sở dữ liệu. Nó thoát với EXC_BAD_ACCESS và tôi không thấy bất kỳ lỗi nào. Ví dụ, lần cuối cùng nó bỏ nó kết thúc vào lúc sqlite3_step:

sqlite3_stmt *statement; 
const char *query = "INSERT OR IGNORE INTO `names` (`id`,`name`) VALUES (?,?);"; 
if(sqlite3_prepare_v2(database, query, -1, &statement, NULL) != SQLITE_OK){ 
    NSAssert1(0, @"Error while creating insert statement. '%s'", sqlite3_errmsg(database)); 
    return NO; 
} 
sqlite3_bind_int(statement, 1, id); 
sqlite3_bind_text(statement, 2, name, -1, SQLITE_TRANSIENT); 

if(sqlite3_step(statement) != SQLITE_DONE) 
    NSAssert1(0, @"Error while inserting. '%s'", sqlite3_errmsg(database)); 

sqlite3_finalize(statement); 

Nó không phải luôn luôn bỏ vào sqlite3_step, đôi khi nó bỏ vào sqlite3_prepare_v2 hoặc sqlite3_exec. Tôi đã thử đặt các câu lệnh này trong một vòng lặp và thử lại nếu nó không trả về OK, nhưng điều đó không hoạt động:

int returnCode = 0; 
do{ 
    returnCode = sqlite3_step(statement); 
    if(returnCode != SQLITE_DONE){ 
     usleep(20); 
    } 
}while(returnCode != SQLITE_DONE); 

Tôi cũng đã thử các giao dịch SQL, nhưng điều đó không tạo ra bất kỳ sự khác biệt nào . Làm sao tôi có thể giải quyết việc này? Nó có vẻ như nó là một vấn đề đồng thời khá cơ bản nhưng tôi đã không nhìn thấy bất cứ điều gì mà làm việc cho tôi.

Cảm ơn bạn cho tất cả sự giúp đỡ của bạn, Justin

+0

Ứng dụng * thoát *? Chỉ để được tò mò, nó chỉ là một số loại xây dựng cụ thể của Apple? Trong vani Sqlite nó chỉ trả về lỗi 'SQLITE_LOCKED' nếu có tranh chấp, và bạn cũng có thể cài đặt một trình xử lý bận để đưa ra quyết định về việc thử lại trong những trường hợp này. –

Trả lời

1

Tôi đang trong quá trình viết một chương trình trong Objective-C đó là hành vi w.r.t gần giống hệt nhau.

Dưới đây là làm thế nào tôi có ý định để đồng bộ hóa truy cập (Các câu hỏi tôi hỏi có loại là không liên quan, nhưng có một cái nhìn vào mã):

Calling sqlite3_close for a static sqlite3* handle

Tôi sẽ sử dụng một ví dụ NSLock tĩnh và khóa nó trong khi viết, và sau đó mở khóa khi tôi hoàn thành.

Tôi không biết có bao nhiêu thay đổi sẽ áp dụng cho ứng dụng của bạn, nhưng đó có thể là giải pháp.

2

Trừ khi bạn biên dịch lại bằng cài đặt đặc biệt, SQLite không phải là chuỗi an toàn.

Xem http://www.sqlite.org/faq.html#q6

Vì vậy, nó tùy thuộc vào bạn để chăm sóc truy cập vào DB và gọi hoạt động SQL vào nó từ chủ đề tương tự.

Tuy nhiên, tôi đã đưa ra một giải pháp trên mặt của tôi mà có vẻ là ok ngay cả trong môi trường đa luồng: Tôi đảm bảo rằng bất kỳ hoạt động SQLite nào được bảo vệ bằng chỉ thị @synchronized để đảm bảo rằng một khi chuỗi đang làm điều gì đó trên DB, bất kỳ chủ đề khác được ngăn chặn truy cập nó. Vì vậy, thay vì nói "tất cả các hoạt động SQlite phải được thực hiện trong cùng một luồng", tôi muốn nói "đảm bảo rằng hai hoạt động không được thực hiện song song trong các chủ đề khác nhau".

0

Tôi gặp vấn đề tương tự trong ứng dụng hoạt động giống như vậy. Bất cứ khi nào thread cập nhật dữ liệu từ internet bắt đầu ghi vào cơ sở dữ liệu cùng lúc khi tôi thực hiện một số tương tác UI đã kích hoạt truy cập của DB, chương trình đã bị lỗi.

@ các câu lệnh không đồng bộ trên mọi truy vấn cơ sở dữ liệu bên trong trình xử lý DB của tôi dường như giải quyết được sự cố.

1

Tôi không chắc đây có phải là giải pháp hợp lệ hay không, nhưng điều tôi muốn làm là tải xuống tất cả dữ liệu trong một chuỗi riêng biệt. Nhưng khi điều này được thực hiện tải về, trở về chủ đề chính, và làm chèn của bạn trong chủ đề chính.

dispatch_async(dispatch_get_global_queue(0, 0), ^{ 

    //download data from internet 

    dispatch_async(dispatch_get_main_queue(), ^{ 
     //update database here 
    } 
} 

Bằng cách này bạn không gặp phải bất kỳ vấn đề đa luồng nào có thể xảy ra. Kể từ khi tải xuống là những gì sẽ mất nhiều thời gian nhất, nó được thực hiện trong một chủ đề khác, nhưng việc cập nhật cơ sở dữ liệu không nên mất nhiều thời gian ... Vì vậy, nó sẽ chỉ giữ chủ đề chính cho một khoảng thời gian gần như không đáng kể. Ít nhất nó nên nếu các truy vấn không chậm, và không có tấn của họ.

0

Kể từ phiên bản 3.5.0, bạn có thể chia sẻ cùng một kết nối cơ sở dữ liệu giữa nhiều luồng: http://www.sqlite.org/34to35.html Kiểm tra phiên bản của SQLite mà bạn đang sử dụng.

Ngoài ra, hãy kiểm tra hàm sqlite3_threadsafe.

Tôi đã viết chương trình C++ chia sẻ kết nối cơ sở dữ liệu giữa hai chủ đề và không nhận được lỗi seg (Tôi tin rằng đó là điều tương tự như EXC_BAD_ACCESS): https://gist.github.com/allyourcode/7428159 Ví dụ đó cho thấy việc sử dụng cơ sở dữ liệu trong bộ nhớ. kết quả với cơ sở dữ liệu được bảo vệ bằng đĩa.

Tôi muốn phân tích điều này bằng công cụ đua dữ liệu, chẳng hạn như tsan, nhưng tôi cần tìm ra cách thực hiện: P

Các vấn đề liên quan