我正在为python中的气象站编写一些软件。每分钟,每分钟都会从传感器中获取新值(这些值实际上取自每分钟更新的文件)并添加到数组中。此数组中的项目将上载到联机数据库。如果例如因特网连接断开,则阵列充当缓冲器以阻止当前最新数据被新版本覆盖。当连接恢复时,阵列中的项目都可以上传,任何内容都不会丢失。数组的填充发生在一个单独的线程中。
回到主线程,1分钟后,制作一个数组副本。然后迭代该副本并将每个项目上载到数据库。在上传项目之后,它将从ORIGINAL数组中删除,但不会从副本中删除,以防止因操作迭代而导致的错误。如果上传任何项目时发生错误,则整个迭代停止并在下一分钟恢复,以便保留数据库中数据的时间顺序。这是线程安全的,因为我正在从原始数组中删除,但我正在迭代它的副本,我发现代码中任何地方的线程方面都没有问题。
但是,我很难删除看起来像是在迭代时修改列表时会发现的问题,即使这在任何地方都没有发生。当每分钟提供一个数据时(这是正常操作),一切都上传得很好。此外,如果阵列中有两个项目(通过删除一分钟的互联网连接完成),它也可以正常工作。两个项目都按正确的顺序上传。但是,如果列表中上传的项目超过两个(互联网停机时间超过一分钟),则迭代似乎会跳过列表中的其他所有项目。这导致上传所有其他项目,然后上载阵列中剩余项目的每个其他项目,依此类推,直到阵列每分钟返回正常项目。这导致数据库中的上传失序。
我已将错误缩小到从uploadBuffer数组中删除项目的行,因为没有此行它可以正常工作(显然,上传后项目不会从缓冲区中删除)。我没有看到这个问题是如何存在的,因为我正在迭代原始列表的副本。从原始列表中删除项目是使用其值而不是其索引,因此这不是问题。此外,在迭代开始之前,原始数组的副本会生成一次,因此我不会看到从原始数据中删除项目如何影响副本上的迭代。
我真的很想解决这个问题,因为我已经尝试了两周以上没有运气,所以感谢您的帮助,我的代码如下。另外,如果您发现我使用线程锁的方式有任何问题,请提及,因为我不能完全确定我是否正确使用它们。
import urllib2
from lxml import etree
import os
import datetime
import time
import httplib
import socket
import threading
timeFormat = "%d/%m/%Y at %H:%M:%S.%f"
rollingData = "/home/pi/weatherstation/other/latest.xml"
bufferLock = threading.Lock()
print("MOD_UPLOAD: file watching started on " +
datetime.datetime.now().strftime(timeFormat))
uploadBuffer = []
def watchForReport():
previousModified = os.stat(rollingData)[8]
while True:
try:
currentModified = os.stat(rollingData)[8]
# New data needs to be uploaded
if not currentModified == previousModified:
newData = open(rollingData, "r").read()
bufferLock.acquire()
uploadBuffer.append(newData)
bufferLock.release()
previousModified = currentModified
except:
print("OSError")
# Watch for new data in separate thread
threading.Thread(target = watchForReport).start()
while True:
currentSecond = datetime.datetime.now().strftime("%S")
if currentSecond == "01" or currentSecond == "02":
if bufferLock.locked() == False:
bufferLock.acquire()
bufferCopy = uploadBuffer
bufferLock.release()
# If there are records to be uploaded
if len(bufferCopy) > 0:
for item in bufferCopy:
print("\nreport: " + item[:35])
print("bufferCount: " + str(len(bufferCopy)))
if item == "":
# This isn't the problem, as the text 'empty' is almost never printed
print("empty")
bufferCopy.remove(item)
break
else:
report = etree.fromstring(item)
# Separate date and time and format
timestamp = report.xpath("@time")[0]
tDate = timestamp.rsplit("T", 2)[0]
tTime = timestamp.rsplit("T", 2)[1]
tTime = tTime.replace(":", "-")
# Generate URL to write data to the database
url = "http://www.example.com/uploadfile.php?date="
url += tDate + "&time=" + tTime + "&shieldedtemp="
url += report.xpath("@shieldedtemp")[0] + "&exposedtemp="
url += report.xpath("@exposedtemp")[0] + "&soil10temp="
url += report.xpath("@soil10temp")[0] + "&soil30temp="
url += report.xpath("@soil30temp")[0] + "&soil100temp="
url += report.xpath("@soil100temp")[0]
try:
# Upload report to database
response = urllib2.urlopen(url, timeout = 9).read()
print("response: " + response)
# Response[4] means success
if response == "response[4]":
bufferLock.acquire()
# Thus is the line causing the problem:
uploadBuffer.remove(item)
bufferLock.release()
else:
# Break out of loop on failure to preserve chronology
break
except urllib2.HTTPError:
print("urllib2.HTTPError")
print("bufferReCount: " + str(len(bufferCopy)))
break
except httplib.BadStatusLine:
print("httplib.BadStatusLine")
print("bufferReCount: " + str(len(bufferCopy)))
break
except urllib2.URLError:
print("urllib2.URLError")
print("bufferReCount: " + str(len(bufferCopy)))
break
except socket.timeout:
print("timeout")
print("bufferReCount: " + str(len(bufferCopy)))
break
print("bufferReCount: " + str(len(bufferCopy)))